Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user test #5

Open
wants to merge 25 commits into
base: 5.5
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
30 changes: 21 additions & 9 deletions src/test/functional/data.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
export const normalUserTestData = {
password: "QAtpx123#",
userName: "Normal User"
};

export const adminUserTestData = {
password: "QAtpx123#",
userName: "Admin User"
};
export const normalUserTestData = {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
password: 'QAtpx123#',
userName: 'Normal User',
};

export const adminUserTestData = {
password: 'QAtpx123#',
userName: 'Admin User',
};

export const adminUserTestDataNOTlocalhost = {
userName: 'Admin',
password: 'admin123',
};

export const newEmployeeData = {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
newEmployeeName: 'FirstName',
newEmployeeMiddleName: 'MiddleName',
newEmployeeLastName: 'lastName',
password: 'Password123',
};
6 changes: 6 additions & 0 deletions src/test/functional/locators.ts
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const loginPageLocator = {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
nameInput: '[name="username"]',
passwordInput: '[name="password"]',
loginButton: '[type="submit"]',
userNameAfterLogin: '.oxd-userdropdown-name',
};
11 changes: 4 additions & 7 deletions src/test/functional/pages/BasePage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Page, Browser, chromium, Locator } from "@playwright/test";
import { Browser, chromium, Page } from '@playwright/test';

annatooploox marked this conversation as resolved.
Show resolved Hide resolved
import config from "../../playwright.config";
import config from '../../playwright.config';

export class BasePage {
private browser: Browser | null = null;
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -25,10 +25,7 @@ export class BasePage {
await this.page.waitForURL(config.baseUrl);
}

annatooploox marked this conversation as resolved.
Show resolved Hide resolved
async navigateToSubPage(locator: Locator): Promise<void> {
if (typeof locator === "string") {
locator = this.page.locator(locator);
}
await locator.click();
public async navigateToSubPage(pageName: string): Promise<void> {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
await this.page.getByRole('link', { name: pageName }).click();
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
}
}
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
67 changes: 67 additions & 0 deletions src/test/functional/pages/PersonalDetailsPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { expect } from '@playwright/test';
import { Locator, Page } from 'playwright';

import { randomNumber } from '../utils/utils';

const nationality = 'Algerian',
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
nickname = 'NicknameTest',
maritalStatus = 'Married',
gender = 'Female',
smoker = 'Yes';

export class PersonalDetailsPage {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
readonly page: Page;
readonly pageHeader: Locator;
readonly selectDropdown: Locator;

constructor(page: Page) {
this.page = page;
this.pageHeader = page.getByRole('heading', { name: 'Personal Details' });
this.selectDropdown = page.getByText('-- Select --');
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
}

public async verifyIfPersonalDetailsPageIsOpened(): Promise<void> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not very useful...
await expect(somePageThatIUseInTest.pageHeader).toBeVisible(); is very readable and doesn't need wrapping IMO

await expect(this.pageHeader).toBeVisible();
}

public async fillPersonalDetails(): Promise<void> {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
await this.fillTextboxField('Nickname', 3, nickname);
await this.fillTextboxField('Other Id', 1, randomNumber);
await this.fillTextboxField("Driver's License Number", 2, randomNumber);
await this.fillDatePicker('License Expiry Date', '2020-02-02');
await this.fillDatePicker('Date of Birth', '2000-01-01');
await this.selectDropdown.nth(1).click();
await this.chooseOptionFromDropdown(maritalStatus);
await this.clickCheckbox(gender, 'span');
await this.selectDropdown.first().click();
await this.chooseOptionFromDropdown(nationality);
await this.clickCheckbox(smoker, 'i');
}

async fillDatePicker(text: string, date: string): Promise<void> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't be needed after adding getTextboxByTextLabel method to BasePage (and extending it by this class)

await this.page
.locator('form div')
.filter({
hasText: text,
})
.getByPlaceholder('yyyy-mm-dd')
.fill(date);
}

async fillTextboxField(label: string, nth: number, text: string): Promise<void> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't be needed after adding getTextboxByTextLabel method to BasePage (and extending it by this class).
And - nth is not a proper way to define Locators.
If layout will change and another field will be added between some fields, test most probably will go with no problem, but the data will land in another field instead.

await this.page
.locator('form div')
.filter({ hasText: label })
.getByRole('textbox')
.nth(nth)
.fill(text);
}

async chooseOptionFromDropdown(option: string): Promise<void> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wraping this isn't very useful, especially if option is pretty generic, but well... I get it somehow... please use exact: true in options and perhaps move it to BasePage instead?
then it can be public (because it would possibly be used by tests) or protected if you're not planning to use it in tests

await this.page.getByRole('option', { name: option }).click();
}

async clickCheckbox(label: string, locator: string): Promise<void> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well... clickCheckbox that accepts 'span' as a locator?
Please consider to create BasePage universal method similar to getTextboxByTextLabel (mentioned earlier) - I quess all theirs checkboxes are toggles so to avoid confusion you can name it getToggleByTextlLabel(textLabel: string).

await this.page.locator('label').filter({ hasText: label }).locator(locator).click();
}
}
59 changes: 58 additions & 1 deletion src/test/functional/pages/PimPage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,63 @@
import { BasePage } from "./BasePage";
import { Locator, Page } from 'playwright';

import { newEmployeeData } from '../data';

annatooploox marked this conversation as resolved.
Show resolved Hide resolved
import { BasePage } from './BasePage';
import { PersonalDetailsPage } from './PersonalDetailsPage';

const randomNumber = (Math.random() * 1000).toString().substring(0, 3);
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
export class PimPage extends BasePage {
readonly page: Page;
readonly pimPageButton: Locator;
readonly addButton: Locator;
readonly nameInput: Locator;
readonly middleNameInput: Locator;
readonly lastNameInput: Locator;
readonly employeeId: Locator;
readonly createLoginDetailsCheckbox: Locator;
readonly username: Locator;
readonly password: Locator;
readonly passwordConfirmed: Locator;
readonly saveButton: Locator;
readonly cancelButton: Locator;
readonly personalDetailsPage: Page;

constructor(page: Page) {
super(page);
this.page = page;
this.pimPageButton = page.getByRole('link', { name: 'PIM' });
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
this.addButton = page.getByRole('button', { name: 'Add' });
this.nameInput = page.getByPlaceholder('First Name');
this.middleNameInput = page.getByPlaceholder('Middle Name');
this.lastNameInput = page.getByPlaceholder('Last Name');
this.employeeId = page.locator('input:below(:text("Employee Id"))').first();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use getTextboxByTextLabel mentioned earlier

this.createLoginDetailsCheckbox = page
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
.locator('input:below(:text("Create Login Details"))')
.first();
this.username = page.locator('div:has-text(“Username”) + div').first();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use getTextboxByTextLabel mentioned earlier

this.passwordConfirmed = page.locator('input[type="password"]').nth(1);
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
this.saveButton = page.getByRole('button', { name: 'Save' });
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
}

public async addEmployeeWithLoginCredentials(): Promise<void> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please see fillPersonalDetails comment and do similarly (you can even use the same UserData type)

await this.addButton.click();
await this.nameInput.fill(newEmployeeData.newEmployeeName + ' ' + randomNumber);
await this.middleNameInput.fill(newEmployeeData.newEmployeeMiddleName);
await this.lastNameInput.fill(newEmployeeData.newEmployeeLastName);
await this.employeeId.fill('' + randomNumber);
await this.createLoginDetailsCheckbox.click();
await this.username.fill(newEmployeeData.newEmployeeName + ' ' + randomNumber);
await this.password.fill(newEmployeeData.password);
await this.passwordConfirmed.fill(newEmployeeData.password);
await this.saveButton.click();
}

public async fillNewEmployeePersonalDetails(): Promise<void> {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
const personalDetailsPage = new PersonalDetailsPage(this.page);
await personalDetailsPage.verifyIfPersonalDetailsPageIsOpened();
await personalDetailsPage.fillPersonalDetails();
await this.saveButton.click();
await this.page.getByText('Success').isVisible();
}
}
27 changes: 27 additions & 0 deletions src/test/functional/tests/addUser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { expect, test } from '@playwright/test';

annatooploox marked this conversation as resolved.
Show resolved Hide resolved
import { adminUserTestData } from '../data';
import { LoginPage } from '../pages/LoginPage';
import { PimPage } from '../pages/PimPage';

let loginPage: LoginPage, pimPage: PimPage;

test.describe('Adding a new employee', () => {
test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
pimPage = new PimPage(page);
await loginPage.initialize();
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
await loginPage.loginUser(adminUserTestData.userName, adminUserTestData.password);
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
await loginPage.navigateToSubPage('PIM');
await expect(page.getByRole('heading', { name: 'Employee Information' })).toBeVisible();
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
});

test.afterEach(async () => {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
await loginPage.close();
});

test('Add new employee user with account & fill new employee personal details', async () => {
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
await pimPage.addEmployeeWithLoginCredentials();
await pimPage.fillNewEmployeePersonalDetails();
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
});
annatooploox marked this conversation as resolved.
Show resolved Hide resolved
});
1 change: 1 addition & 0 deletions src/test/functional/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const randomNumber = (Math.random() * 1000).toString().substring(0, 3);
56 changes: 27 additions & 29 deletions src/test/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/test/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const config: CustomConfig = {
{
name: 'desktop-chromium',
use: {
browserName: 'chromium',
headless: false,
viewport: { width: 1280, height: 720 },
screenshot: 'only-on-failure',
Expand All @@ -34,7 +35,7 @@ const config: CustomConfig = {
maxDiffPixelRatio: 0.1,
},
},
baseUrl: 'http://localhost:8888/web/index.php/auth/login'
baseUrl: 'http://localhost:8888/',
};

export default config;