Skip to content

Commit

Permalink
Permafail E2E: fix Notifications spec. (#78616)
Browse files Browse the repository at this point in the history
* packages/calypso-e2e/src/secrets/encrypted.enc
- migrate commentingUser to the new schema.

* packages/calypso-e2e/src/lib/components/notifications-component.ts
- refactor the component POM to use modern meethods.

packages/calypso-e2e/src/lib/test-account.ts
- add new flag for `authenticate` call to allow waiting for the full load.

test/e2e/specs/infrastructure/notification__actions.ts
- refactor the spec to only perform one action, not to perform and undo the action.

* test/e2e/specs/infrastructure/notification__actions.ts
- explain the purpose of the spec with JSDoc.
- rename the file.

packages/calypso-e2e/src/lib/test-account.ts
- change optional parameter for `authenticate` to "waitUntiLStable".
  • Loading branch information
worldomonation authored Jun 27, 2023
1 parent 223926c commit 0e0c171
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 172 deletions.
55 changes: 13 additions & 42 deletions packages/calypso-e2e/src/lib/components/notifications-component.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import { Page } from 'playwright';
import { Locator, Page } from 'playwright';

const selectors = {
// Notifications panel (including sub-panels)
activeSingleViewPanel: '.wpnc__single-view.wpnc__current',
notification: ( text: string ) => `.wpnc__comment:has-text("${ text }")`,

// Comment actions
commentAction: ( action: string ) => `button.wpnc__action-link:has-text("${ action }"):visible`,
undoLocator: '.wpnc__undo-item',
undoLink: '.wpnc__undo-link',
};
/**
* Component representing the notifications panel and notifications themselves.
*/
export class NotificationsComponent {
private page: Page;
private anchor: Locator;

/**
* Constructs an instance of the component.
Expand All @@ -23,6 +14,8 @@ export class NotificationsComponent {
*/
constructor( page: Page ) {
this.page = page;
// There is no accessible locator for this panel.
this.anchor = page.locator( 'div[id=wpnc-panel]' );
}

/**
Expand All @@ -31,51 +24,29 @@ export class NotificationsComponent {
* @param {string} text Text contained in the notification.
* @returns {Promise<void>} No return value.
*/
async clickNotification( text: string ): Promise< void > {
await this.page.click( selectors.notification( text ) );
async openNotification( text: string ): Promise< void > {
await this.anchor.getByText( text ).click();
}

/**
* Given a string of text, click on the button in expanded single notification view to execute the action.
*
* eg. 'Trash' -> Clicks on the 'Trash' button when viewing a single notification.
*
* @param {string} action Predefined list of strings that are accepted.
* @returns {Promise<void>} No return value.
* @param {string} action Action to perform on the notification.
*/
async clickNotificationAction( action: string ): Promise< void > {
// we need to make sure we're in a specific notification view before proceeding with the individual action
const elementHandle = await this.page.waitForSelector( selectors.activeSingleViewPanel );
await elementHandle.waitForElementState( 'stable' );
await this.page.click( selectors.commentAction( action ) );
await this.anchor
.getByRole( 'list' )
.getByRole( 'button', { name: action, exact: true } )
.click();
}

/**
* Clicks the undo link to undo the previous action.
*
* @returns {Promise<void>} No return value.
*/
async clickUndo(): Promise< void > {
await this.waitForUndoMessage();
await this.page.click( selectors.undoLink );
}

/**
* Waits for undo message to appear
*
* @returns {Promise<void>} No return value.
*/
async waitForUndoMessage(): Promise< void > {
await this.page.waitForSelector( selectors.undoLocator );
}

/**
* Waits for undo message to disappear
*
* @returns {Promise<void>} No return value.
*/
async waitForUndoMessageToDisappear(): Promise< void > {
await this.waitForUndoMessage();
await this.page.waitForSelector( selectors.undoLocator, { state: 'hidden' } );
await this.anchor.getByRole( 'button', { name: 'Undo', exact: true } ).click();
await this.anchor.getByText( 'Comment trashed' ).waitFor( { state: 'detached' } );
}
}
10 changes: 9 additions & 1 deletion packages/calypso-e2e/src/lib/test-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { EmailClient } from '../email-client';
import envVariables from '../env-variables';
import { SecretsManager } from '../secrets';
import { TOTPClient } from '../totp-client';
import { SidebarComponent } from './components/sidebar-component';
import { LoginPage } from './pages/login-page';
import type { TestAccountCredentials } from '../secrets';

Expand All @@ -34,7 +35,10 @@ export class TestAccount {
* @param {Page} page Page object.
* @param {string} [url] URL to expect once authenticated and redirections are finished.
*/
async authenticate( page: Page, { url }: { url?: string | RegExp } = {} ): Promise< void > {
async authenticate(
page: Page,
{ url, waitUntilStable }: { url?: string | RegExp; waitUntilStable?: boolean } = {}
): Promise< void > {
const browserContext = page.context();
await browserContext.clearCookies();

Expand All @@ -50,6 +54,10 @@ export class TestAccount {
if ( url ) {
await page.waitForURL( url, { timeout: 20 * 1000 } );
}
if ( waitUntilStable ) {
const sidebarComponent = new SidebarComponent( page );
await sidebarComponent.waitForSidebarInitialization();
}
}

/**
Expand Down
Binary file modified packages/calypso-e2e/src/secrets/encrypted.enc
Binary file not shown.
129 changes: 0 additions & 129 deletions test/e2e/specs/infrastructure/notification__actions.ts

This file was deleted.

130 changes: 130 additions & 0 deletions test/e2e/specs/infrastructure/notifications__general-interaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* @group calypso-pr
*/

import {
DataHelper,
NavbarComponent,
NotificationsComponent,
RestAPIClient,
TestAccount,
NewCommentResponse,
PostResponse,
} from '@automattic/calypso-e2e';
import { Page, Browser } from 'playwright';

declare const browser: Browser;

/**
* Tests general interaction with the notification panel, running through
* all actions once.
*/
describe( 'Notifications: General Interactions', function () {
const comment = DataHelper.getRandomPhrase() + ' notification-actions-spec';

// TestAccount and RestAPI instances.
let commentingUser: TestAccount;
let notificationsUser: TestAccount;
let commentingUserRestAPIClient: RestAPIClient;
let notificationUserRestAPIClient: RestAPIClient;

// API responses.
let newPost: PostResponse;
let newComment: NewCommentResponse;

let notificationsComponent: NotificationsComponent;
let page: Page;

beforeAll( async function () {
// Create an instance of RestAPI as the user making the comment.
commentingUser = new TestAccount( 'commentingUser' );
commentingUserRestAPIClient = new RestAPIClient( commentingUser.credentials );

// Create an instance of RestAPI as the user receiving notification.
notificationsUser = new TestAccount( 'notificationsUser' );
notificationUserRestAPIClient = new RestAPIClient( notificationsUser.credentials );

// Create a new post and store the response.
newPost = await notificationUserRestAPIClient.createPost(
notificationsUser.credentials.testSites?.primary.id as number,
{ title: DataHelper.getRandomPhrase() }
);

// Create a new comment on the post as the commentingUser and
// store the response.
newComment = await commentingUserRestAPIClient.createComment(
notificationsUser.credentials.testSites?.primary.id as number,
newPost.ID,
comment
);

// Log in as the user receiving the notification.
page = await browser.newPage();
await notificationsUser.authenticate( page, { waitUntilStable: true } );
} );

it( 'Open Notifications panel', async function () {
const navbarComponent = new NavbarComponent( page );
await navbarComponent.openNotificationsPanel();
} );

it( 'Click notification for the comment', async function () {
notificationsComponent = new NotificationsComponent( page );
await notificationsComponent.openNotification( comment );
} );

it( 'Approve comment', async function () {
await notificationsComponent.clickNotificationAction( 'Approve' );
} );

it( 'Like comment', async function () {
await notificationsComponent.clickNotificationAction( 'Like' );
} );

it( 'Mark comment as spam', async function () {
await notificationsComponent.clickNotificationAction( 'Spam' );
await notificationsComponent.clickUndo();
} );

it( 'Trash comment', async function () {
await notificationsComponent.clickNotificationAction( 'Trash' );
} );

afterAll( async function () {
if ( ! newComment ) {
return;
}

// Clean up the comment.
try {
await notificationUserRestAPIClient.deleteComment(
notificationsUser.credentials.testSites?.primary.id as number,
newComment.ID
);
} catch ( e: unknown ) {
console.warn(
`Failed to clean up test comment in notification_action spec for site ${
notificationsUser.credentials.testSites?.primary.id as number
}, comment ${ newComment.ID }`
);
}

if ( ! newPost ) {
return;
}

// Clean up the post.
try {
await notificationUserRestAPIClient.deletePost(
notificationsUser.credentials.testSites?.primary.id as number,
newPost.ID
);
} catch ( e: unknown ) {
console.warn(
`Failed to clean up test comment in notification_action spec for site ${
notificationsUser.credentials.testSites?.primary.id as number
}, comment ${ newComment.ID }`
);
}
} );
} );

0 comments on commit 0e0c171

Please sign in to comment.