Skip to content

Commit

Permalink
Merge pull request #1358 from sillsdev/chore/changePasswordTests
Browse files Browse the repository at this point in the history
Change password playwright tests
  • Loading branch information
rmunn authored Mar 25, 2022
2 parents 5e6d7a5 + 188262c commit 8bb4430
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 17 deletions.
64 changes: 64 additions & 0 deletions test/e2e/change-password.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { expect } from '@playwright/test';
import { test } from './utils/fixtures';
import { ChangePasswordPage } from './pages/change-password.page';
import { changePassword } from './utils/testControl';
import { LoginPage } from './pages/login.page';
import { PageHeader } from './pages/page-header.page';

test.describe('E2E Change Password app', () => {
const newPassword = '12345678';
let changePasswordPage: ChangePasswordPage;

test.beforeAll(async ({ memberTab }) => {
changePasswordPage = new ChangePasswordPage(memberTab);
await changePasswordPage.goto();
});

test.afterAll(async ({ member, request }) => {
// reset password back to original
await changePassword(request, member.username, member.password);
});

test('Refuses to allow form submission if the confirm input does not match', async () => {
await changePasswordPage.passwordInput.fill(newPassword);
await changePasswordPage.confirmInput.fill('blah12345');
await expect (changePasswordPage.submitButton).toBeDisabled();
});

test('Allows form submission if the confirm input matches', async () => {
await changePasswordPage.passwordInput.fill(newPassword);
await changePasswordPage.confirmInput.fill(newPassword);
await expect(changePasswordPage.submitButton).toBeEnabled();
});

test('Should not allow a password less than 7 characters', async () => {
let shortPassword = '12345';
await changePasswordPage.passwordInput.fill(shortPassword);
await changePasswordPage.confirmInput.fill(shortPassword);
await expect (changePasswordPage.submitButton).toBeDisabled();
});

test('Can successfully change user\'s password after form submission', async ({ page, member }) => {
await changePasswordPage.passwordInput.fill(newPassword);
await changePasswordPage.confirmInput.fill(newPassword);
await expect (changePasswordPage.passwordMatchImage).toBeVisible();
await expect (changePasswordPage.submitButton).toBeEnabled();
await changePasswordPage.submitButton.click();
// when password is changed successfully, a notice appears on the page
const messageSuccessfulUpdate = '[data-ng-bind-html="notice.message"] >> text=Password updated successfully';
await changePasswordPage.page.waitForSelector(messageSuccessfulUpdate, {strict: false, state: 'attached'});
expect (await changePasswordPage.noticeList.locator(messageSuccessfulUpdate).count()
).toBeGreaterThan(0);

// test login with new password

// await logout(memberTab); // CANNOT do this as it invalidates the session stored in storageState.json! - 2022-03 RM
// await login(memberTab, memberTab.username, newPassword);

const loginPage = new LoginPage(page);
await loginPage.loginAs(member.username, newPassword);
const pageHeader = new PageHeader(page);
await expect (pageHeader.myProjects.button).toBeVisible();
});

});
21 changes: 21 additions & 0 deletions test/e2e/pages/page-header.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Locator, Page } from "@playwright/test";

type MyProjects = {
button: Locator;
links: Locator;
};

export class PageHeader {
readonly page: Page;
readonly myProjects: MyProjects;
readonly loginButton: Locator;

constructor(page: Page) {
this.page = page;
this.myProjects = {
button: page.locator('#myProjectDropdownButton'),
links: page.locator('#myProjectDropdownMenu >> .dropdown-item')
};
this.loginButton = page.locator('text=Login').nth(0);
}
}
2 changes: 0 additions & 2 deletions test/e2e/pages/projects.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,4 @@ export class ProjectsPage {
await this.page.goto(ProjectsPage.url);
await expect(this.pageName).toBeVisible();
}

// TODO: write feature request: implement a waiting spinning somthing indicator - create github issue as feature request
}
72 changes: 59 additions & 13 deletions test/e2e/utils/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,78 @@ import type { Browser, Page } from '@playwright/test';
import type { usernamesForFixture } from './userFixtures';
import constants from '../testConstants.json';

export type UserTab = Page & {
export type UserDetails = {
username: string,
password: string,
name: string,
email: string,
}

export type UserTab = Page & UserDetails;

function setupUserDetails(obj: any, username: usernamesForFixture) {
obj.username = constants[`${username}Username`] ?? username;
obj.name = constants[`${username}Name`] ?? username;
obj.password = constants[`${username}Password`] ?? 'x';
obj.email = constants[`${username}Email`] ?? `${username}@example.com`;
}

const userTab = (username: usernamesForFixture) => async ({ browser, browserName }: { browser: Browser, browserName: string}, use: (r: UserTab) => Promise<void>) => {
const storageState = `${browserName}-${username}-storageState.json`;
const context = await browser.newContext({ storageState })
const page = await context.newPage();
const tab = page as UserTab;
tab.username = constants[`${username}Username`] ?? username;
tab.name = constants[`${username}Name`] ?? username;
tab.password = constants[`${username}Password`] ?? 'x';
tab.email = constants[`${username}Email`] ?? `${username}@example.com`;
setupUserDetails(tab, username);
await use(tab);
await tab.close();
await context.close();
}

// Extend basic test by providing a "todoPage" fixture.
// Add user fixtures to test function
// Two kinds of fixtures: userTab and user, where "user" is one of "admin", "manager", "member", "member2", or "observer"
// The userTab fixture represents a browser tab (a "page" in Playwright terms) that's already logged in as that user
// The user fixture just carries that user's details (username, password, name and email)
// Note: "Tab" was chosen instead of "Page" to avoid confusion with Page Object Model classes like SiteAdminPage
export const test = (base
.extend<{ adminTab: UserTab }>({ adminTab: userTab('admin') })
.extend<{ managerTab: UserTab }>({ managerTab: userTab('manager') })
.extend<{ memberTab: UserTab }>({ memberTab: userTab('member') })
.extend<{ member2Tab: UserTab }>({ member2Tab: userTab('member2') })
.extend<{ observerTab: UserTab }>({ observerTab: userTab('observer') })
.extend<{
adminTab: UserTab,
managerTab: UserTab,
memberTab: UserTab,
member2Tab: UserTab,
observerTab: UserTab,
admin: UserDetails,
manager: UserDetails,
member: UserDetails,
member2: UserDetails,
observer: UserDetails,
}>({
adminTab: userTab('admin'),
managerTab: userTab('manager'),
memberTab: userTab('member'),
member2Tab: userTab('member2'),
observerTab: userTab('observer'),
admin: async ({}, use) => {
let admin = {} as UserDetails;
setupUserDetails(admin, 'admin');
await use(admin);
},
manager: async ({}, use) => {
let manager = {} as UserDetails;
setupUserDetails(manager, 'manager');
await use(manager);
},
member: async ({}, use) => {
let member = {} as UserDetails;
setupUserDetails(member, 'member');
await use(member);
},
member2: async ({}, use) => {
let member2 = {} as UserDetails;
setupUserDetails(member2, 'member2');
await use(member2);
},
observer: async ({}, use) => {
let observer = {} as UserDetails;
setupUserDetails(observer, 'observer');
await use(observer);
}
})
);
5 changes: 3 additions & 2 deletions test/e2e/utils/login.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Browser, Page } from '@playwright/test';
import constants from '../testConstants.json';
import type { usernamesForFixture } from './userFixtures';

export async function login(page: Page, username: string, password: string) {
await page.goto('/auth/login');
Expand All @@ -15,7 +16,7 @@ export async function logout(page: Page) {
return await page.goto('/auth/logout');
}

export function getLoginInfo(name: string) {
export function getLoginInfo(name: usernamesForFixture) {
const usernameKey = `${name}Username`;
const passwordKey = `${name}Password`;
if (Object.hasOwnProperty.call(constants, usernameKey)) {
Expand All @@ -28,7 +29,7 @@ export function getLoginInfo(name: string) {
}
}

export function loginAs(page: Page, name: string) {
export function loginAs(page: Page, name: usernamesForFixture) {
const { username, password } = getLoginInfo(name);
return login(page, username, password);
}
Expand Down

0 comments on commit 8bb4430

Please sign in to comment.