Skip to content

Commit

Permalink
[e2e] Notifications. Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-tsx committed Nov 29, 2023
1 parent 07c81ed commit c5520ef
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 84 deletions.
6 changes: 3 additions & 3 deletions e2e/src/features/notifications.feature
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ Feature: Notifications
And I press Notification Icon Button on the Home page

And I am on the NotificationsList page
And I check that a notification with 'Test Title' title and 'Test description' description is displayed
And I click on the notification with 'Test Title' title and 'Test description' description
And I check that new notification is displayed
And I click on the new notification

And I am on the NotificationContent page
And The Notification Title Text on the Notification Content page has correct Test Title value
Expand Down Expand Up @@ -35,7 +35,7 @@ Feature: Notifications
And I press Notification Icon Button on the Home page
And I am on the NotificationsList page

Then I check that a notification with 'Test Title' title and 'Test description' description is NOT displayed
Then I check that new notification is NOT displayed



Expand Down
39 changes: 22 additions & 17 deletions e2e/src/page-objects/pages/notifications-list.page.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { PreviewItemSelectors } from 'src/lib/notifications/components/notifications/preview-item.selectors';
import type { NotificationInterface } from 'src/lib/notifications/types';

import { VERY_SHORT_TIMEOUT } from 'e2e/src/utils/timing.utils';

import { Page } from '../../classes/page.class';
import { createPageElement, findElement } from '../../utils/search.utils';
import { createPageElement } from '../../utils/search.utils';

export class NotificationsListPage extends Page {
newNotification?: NotificationInterface;
notificationItem = createPageElement(PreviewItemSelectors.notificationItem);
notificationItemTitleText = createPageElement(PreviewItemSelectors.notificationItemTitleText);
notificationItemDescriptionText = createPageElement(PreviewItemSelectors.notificationItemDescriptionText);
Expand All @@ -16,31 +18,34 @@ export class NotificationsListPage extends Page {
await this.notificationItemDescriptionText.waitForDisplayed();
}

async isNotificationDisplayed(title: string, description: string) {
const notificationText = await findElement(
PreviewItemSelectors.notificationItemTitleText,
{ title },
async isNotificationDisplayed({ id, title, description }: NotificationInterface) {
const notificationTextElem = createPageElement(PreviewItemSelectors.notificationItem, { id: String(id) });

await notificationTextElem.waitForDisplayed(
VERY_SHORT_TIMEOUT,
`Notification with ${title} title is not displayed`
);

await findElement(
PreviewItemSelectors.notificationItemDescriptionText,
{ description },
VERY_SHORT_TIMEOUT,
`Notification with ${description} description is not displayed`
);
const titleText = await notificationTextElem
.createChildElement(PreviewItemSelectors.notificationItemTitleText)
.getText();
if (titleText !== title) throw new Error(`Notification title missmatch. Got: ${titleText}`);

return notificationText;
const descriptionText = await notificationTextElem
.createChildElement(PreviewItemSelectors.notificationItemDescriptionText)
.getText();
if (descriptionText !== description) throw new Error(`Notification description missmatch. Got: ${descriptionText}`);
}

async clickOnTheNotification(title: string, description: string) {
const selectedNotification = await this.isNotificationDisplayed(title, description);
await selectedNotification.click();
async clickOnTheNotification({ id }: NotificationInterface) {
const notificationTextElem = createPageElement(PreviewItemSelectors.notificationItem, { id: String(id) });
await notificationTextElem.click();
}

async isNotificationNotDisplayed(title: string, description: string) {
await this.isNotificationDisplayed(title, description).then(
async isNotificationNotDisplayed({ id, title }: NotificationInterface) {
const notificationTextElem = createPageElement(PreviewItemSelectors.notificationItem, { id: String(id) });

await notificationTextElem.waitForDisplayed().then(
() => {
throw new Error(`The notification '${title}' is displayed after turning off 'news' checkbox in settings`);
},
Expand Down
58 changes: 30 additions & 28 deletions e2e/src/step-definitions/notifications.steps.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
import { Given } from '@cucumber/cucumber';
import assert from 'assert';
import axios from 'axios';
import type { NotificationInterface } from 'src/lib/notifications/types';

import { envVars } from 'e2e/src/utils/env.utils';
import { MEDIUM_TIMEOUT } from 'e2e/src/utils/timing.utils';

import { Pages } from '../page-objects';

Given(
/I check that a notification with '(.*)' title and '(.*)' description is displayed/,
{ timeout: MEDIUM_TIMEOUT },
async (title: string, shortDescription: string) => {
await Pages.NotificationsList.isNotificationDisplayed(title, shortDescription);
}
);

Given(
/I check that a notification with '(.*)' title and '(.*)' description is NOT displayed/,
{ timeout: MEDIUM_TIMEOUT },
async (title: string, shortDescription: string) => {
await Pages.NotificationsList.isNotificationNotDisplayed(title, shortDescription);
}
);

Given(
/I click on the notification with '(.*)' title and '(.*)' description/,
{ timeout: MEDIUM_TIMEOUT },
async (title: string, shortDescription: string) => {
await Pages.NotificationsList.clickOnTheNotification(title, shortDescription);
}
);
Given(/I check that new notification is displayed/, { timeout: MEDIUM_TIMEOUT }, async () => {
const notification = Pages.NotificationsList.newNotification;
assert(notification);
await Pages.NotificationsList.isNotificationDisplayed(notification);
});

Given(/I check that new notification is NOT displayed/, { timeout: MEDIUM_TIMEOUT }, async () => {
const notification = Pages.NotificationsList.newNotification;
assert(notification);
await Pages.NotificationsList.isNotificationNotDisplayed(notification);
});

Given(/I click on the new notification/, { timeout: MEDIUM_TIMEOUT }, async () => {
const notification = Pages.NotificationsList.newNotification;
assert(notification);
await Pages.NotificationsList.clickOnTheNotification(notification);
});

Given(/I make request for creating a notification/, { timeout: MEDIUM_TIMEOUT }, async () => {
const currentDate = new Date();
Expand All @@ -49,15 +45,21 @@ Given(/I make request for creating a notification/, { timeout: MEDIUM_TIMEOUT },
expirationDate: expirationDateISO
};

const response = await axios.post('https://temple-api-mainnet.stage.madfish.xyz/api/notifications', requestBody, {
headers: {
'Content-Type': 'application/json',
Authorization: envVars.NOTIFICATION_AUTHORIZATION
const response = await axios.post<NotificationInterface>(
'https://temple-api-mainnet.stage.madfish.xyz/api/notifications',
requestBody,
{
headers: {
'Content-Type': 'application/json',
Authorization: envVars.NOTIFICATION_AUTHORIZATION
}
}
});
);

if (response.status !== 200)
throw new Error(
`Some problems with backend server. Server returns ${response.statusText} with ${response.status} status code`
);

Pages.NotificationsList.newNotification = response.data;
});
2 changes: 1 addition & 1 deletion e2e/src/step-definitions/shared.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Given(
/I got the validation-error '(.*)' in the (.*) on the (.*) page/,
{ timeout: MEDIUM_TIMEOUT },
async (errorName: string, parentElementName: string, pageName: string) => {
const childElement = await createPageElement(`${pageName}/${parentElementName}`).findChildSelectors(
const childElement = await createPageElement(`${pageName}/${parentElementName}`).findChildElementByTestId(
ErrorCaptionSelectors.inputError
);
const getErrorContent = await getElementText(childElement);
Expand Down
40 changes: 19 additions & 21 deletions e2e/src/utils/search.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { ElementHandle } from 'puppeteer';
import { BrowserContext } from '../classes/browser-context.class';
import { MEDIUM_TIMEOUT } from './timing.utils';

const buildTestIDSelector = (testID: string) => `[data-testid="${testID}"]`;

type OtherSelectors = Record<string, string>;

export const findElement = async (
Expand All @@ -20,20 +18,6 @@ export const findElement = async (
return await findElementBySelectors(selector, timeout, errorTitle);
};

const buildChildSelector = (parentTestID: string, childTestID: string) =>
`${parentTestID} [data-testid="${childTestID}"]`;

export const findChildElement = async (
parentTestID: string,
childTestID: string,
timeout = MEDIUM_TIMEOUT,
errorTitle?: string
) => {
const selectors = buildChildSelector(`[data-testid="${parentTestID}"]`, childTestID);

return await findElementBySelectors(selectors, timeout, errorTitle);
};

export const findElementBySelectors = async (selectors: string, timeout = MEDIUM_TIMEOUT, errorTitle?: string) => {
const element = await BrowserContext.page.waitForSelector(selectors, { visible: true, timeout }).catch(error => {
if (errorTitle && error instanceof Error) {
Expand Down Expand Up @@ -64,18 +48,25 @@ export const findElements = async (testID: string) => {
class PageElement {
constructor(public selector: string) {}

createChildElement(testID: string, otherSelectors?: OtherSelectors) {
const childSelector = buildSelector(testID, otherSelectors);
const selectors = buildChildSelector(this.selector, childSelector);

return new PageElement(selectors);
}

findElement(timeout?: number, errorTitle?: string) {
return findElementBySelectors(this.selector, timeout, errorTitle);
}

findChildSelectors(childSelector: string, timeout?: number, errorTitle?: string) {
const selectors = buildChildSelector(this.selector, childSelector);
findChildElementByTestId(childTestId: string, timeout?: number, errorTitle?: string) {
const selectors = buildChildSelectorByTestId(this.selector, childTestId);

return findElementBySelectors(selectors, timeout, errorTitle);
}

waitForDisplayed(timeout?: number) {
return this.findElement(timeout);
waitForDisplayed(timeout?: number, errorTitle?: string) {
return this.findElement(timeout, errorTitle);
}

async click() {
Expand Down Expand Up @@ -137,13 +128,15 @@ export const getElementText = (element: ElementHandle) =>
return textContent;
});

const buildTestIDSelector = (testID: string) => `[data-testid="${testID}"]`;

const buildSelectorPairs = (selectors: OtherSelectors) => {
return Object.entries(selectors).map(([key, val]) =>
val ? (`data-${key}="${val}"` as const) : (`data-${key}` as const)
);
};

const buildSelector = (testID: string, otherSelectors?: OtherSelectors) => {
export const buildSelector = (testID: string, otherSelectors?: OtherSelectors) => {
const pairs = buildSelectorPairs({ ...otherSelectors, testid: testID });
return `[${pairs.join('][')}]`;
};
Expand All @@ -153,6 +146,11 @@ const buildNotSelector = (notSelectors: OtherSelectors) => {
return `:not([${pairs.join(']):not([')}])`;
};

const buildChildSelector = (parentSelector: string, childSelector: string) => `${parentSelector} ${childSelector}`;

const buildChildSelectorByTestId = (parentSelector: string, childTestID: string) =>
buildChildSelector(parentSelector, buildTestIDSelector(childTestID));

export const clearDataFromInput = async () => {
await BrowserContext.page.keyboard.press('End');
await BrowserContext.page.keyboard.down('Shift');
Expand Down
2 changes: 1 addition & 1 deletion src/lib/notifications/components/item/content.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { FC } from 'react';

import { setTestID, TestIDProps } from '../../../analytics';
import { NotificationInterface } from '../../interfaces/notification.interface';
import { NotificationInterface } from '../../types';

type Props = TestIDProps & Pick<NotificationInterface, 'content'>;

Expand Down
10 changes: 3 additions & 7 deletions src/lib/notifications/components/notifications/preview-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Link } from 'lib/woozie';
import { setAnotherSelector, setTestID } from '../../../analytics';
import { NotificationStatus } from '../../enums/notification-status.enum';
import { NotificationType } from '../../enums/notification-type.enum';
import { NotificationInterface } from '../../interfaces/notification.interface';
import type { NotificationInterface } from '../../types';
import { formatDateOutput } from '../../utils/date.utils';
import { PreviewItemSelectors } from './preview-item.selectors';

Expand All @@ -34,6 +34,7 @@ export const NotificationPreviewItem: FC<Props> = ({ notification }) => {
])}
testID={PreviewItemSelectors.notificationItem}
testIDProperties={{ id: notification.id, type: notification.type }}
{...setAnotherSelector('id', notification.id)}
>
<div className="relative">
{notification.status === NotificationStatus.New && (
Expand Down Expand Up @@ -61,16 +62,11 @@ export const NotificationPreviewItem: FC<Props> = ({ notification }) => {
notification.status === NotificationStatus.Read ? 'text-gray-600' : 'text-black'
)}
{...setTestID(PreviewItemSelectors.notificationItemTitleText)}
{...setAnotherSelector('title', notification.title)}
>
{notification.title}
</p>

<p
className="text-gray-600 text-xs"
{...setTestID(PreviewItemSelectors.notificationItemDescriptionText)}
{...setAnotherSelector('description', notification.description)}
>
<p className="text-gray-600 text-xs" {...setTestID(PreviewItemSelectors.notificationItemDescriptionText)}>
{notification.description}
</p>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/notifications/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createAction } from '@reduxjs/toolkit';

import { createActions } from 'lib/store';

import { NotificationInterface } from '../interfaces/notification.interface';
import type { NotificationInterface } from '../types';

export const loadNotificationsAction = createActions<void, NotificationInterface[]>('notifications/LOAD_NOTIFICATIONS');

Expand Down
2 changes: 1 addition & 1 deletion src/lib/notifications/store/state.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createEntity, LoadableEntityState } from 'lib/store';

import { NotificationInterface } from '../interfaces/notification.interface';
import type { NotificationInterface } from '../types';

export interface NotificationsState {
startFromTime: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NotificationPlatformType } from '../enums/notification-platform-type.enum';
import { NotificationStatus } from '../enums/notification-status.enum';
import { NotificationType } from '../enums/notification-type.enum';
import type { NotificationPlatformType } from './enums/notification-platform-type.enum';
import type { NotificationStatus } from './enums/notification-status.enum';
import type { NotificationType } from './enums/notification-type.enum';

interface NotificationLink {
text: string;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/notifications/utils/api.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { templeWalletApi } from 'lib/apis/temple';

import { NotificationPlatformType } from '../enums/notification-platform-type.enum';
import { NotificationStatus } from '../enums/notification-status.enum';
import { NotificationInterface } from '../interfaces/notification.interface';
import type { NotificationInterface } from '../types';

export const loadNotifications$ = (startFromTime: number) =>
from(
Expand Down

0 comments on commit c5520ef

Please sign in to comment.