Skip to content

Commit

Permalink
Merge branch 'main' into gitlab-118
Browse files Browse the repository at this point in the history
  • Loading branch information
shayfaber authored Jan 27, 2025
2 parents d4397be + 80b6aac commit a050f9b
Show file tree
Hide file tree
Showing 18 changed files with 73 additions and 126 deletions.
1 change: 0 additions & 1 deletion bypass-captcha.config.example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* It will set the global cookie to bypass CAPTCHA for Magento 2.
* See: https://github.com/elgentos/magento2-bypass-captcha-cookie
*
* //TODO The extension is WIP. Currently being developed.
*/
import { FullConfig } from '@playwright/test';
import * as playwright from 'playwright';
Expand Down
2 changes: 0 additions & 2 deletions playwright.config.example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,10 @@ export default defineConfig({

/* Configure projects for major browsers */
projects: [
// TODO: uncomment the setup line once authentication works!
// Import our auth.setup.ts file
//{ name: 'setup', testMatch: /.*\.setup\.ts/ },

{
// TODO: uncomment dependency and storage state once authentication works!
name: 'chromium',
testMatch: testFiles,
use: {
Expand Down
73 changes: 45 additions & 28 deletions tests/base/account.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {test, expect} from '@playwright/test';
import {MainMenuPage} from './fixtures/mainmenu.page';
import {LoginPage} from './fixtures/login.page';
import {RegisterPage} from './fixtures/register.page';
import {AccountPage} from './fixtures/account.page';
import {NewsletterSubscriptionPage} from './fixtures/newsletter.page';

Expand All @@ -8,9 +10,6 @@ import inputvalues from './config/input-values/input-values.json';
import selectors from './config/selectors/selectors.json';
import verify from './config/expected/expected.json';

// no resetting storageState, mainmenu has more functionalities when logged in.
// TODO: remove this beforeEach() once authentication as project set-up/fixture works.

// Before each test, log in
test.beforeEach(async ({ page, browserName }) => {
const browserEngine = browserName?.toUpperCase() || "UNKNOWN";
Expand All @@ -32,8 +31,6 @@ test.describe('Account information actions', {annotation: {type: 'Account Dashbo
await page.waitForLoadState();
});

// TODO: add test to update e-mail address

/**
* @feature Magento 2 Change Password
* @scenario User changes their password
Expand All @@ -46,33 +43,52 @@ test.describe('Account information actions', {annotation: {type: 'Account Dashbo
* @then I should see a notification that my password has been updated
* @and I should be able to login with my new credentials.
*/
test('I can change my password',{ tag: '@account-credentials', }, async ({page, browserName}) => {

//TODO: Remove the skip when all issues are fixed.
test.skip('I can change my password',{ tag: '@account-credentials', }, async ({page}) => {
// Create instances and set variables
const mainMenu = new MainMenuPage(page);
const registerPage = new RegisterPage(page);
const accountPage = new AccountPage(page);
let changedPasswordValue = process.env.MAGENTO_EXISTING_ACCOUNT_CHANGED_PASSWORD;
const loginPage = new LoginPage(page);

const browserEngine = browserName?.toUpperCase() || "UNKNOWN";
let randomNumberforEmail = Math.floor(Math.random() * 101);
let emailPasswordUpdatevalue = `passwordupdate-${randomNumberforEmail}-${browserEngine}@example.com`;
let passwordInputValue = process.env.MAGENTO_EXISTING_ACCOUNT_PASSWORD;
let changedPasswordValue = process.env.MAGENTO_EXISTING_ACCOUNT_CHANGED_PASSWORD;

// Log out of current account
if(await page.getByRole('link', { name: selectors.mainMenu.myAccountLogoutItem }).isVisible()){
await mainMenu.logout();
}

// Create account
if(!changedPasswordValue || !passwordInputValue) {
throw new Error("Changed password or original password in your .env file is not defined or could not be read.");
}

// Navigate to Account Information, confirm by checking heading above sidebar
const sidebarAccountInfoLink = page.getByRole('link', { name: 'Account Information' });
sidebarAccountInfoLink.click();
await expect(page.getByRole('heading', { name: 'Account Information' }).locator('span')).toBeVisible();
await registerPage.createNewAccount(inputvalues.accountCreation.firstNameValue, inputvalues.accountCreation.lastNameValue, emailPasswordUpdatevalue, passwordInputValue);

// Update password
await page.goto(slugs.account.changePasswordSlug);
await page.waitForLoadState();
await accountPage.updatePassword(passwordInputValue, changedPasswordValue);

// If login with changePasswordValue is possible, then password change was succesful.
await loginPage.login(emailPasswordUpdatevalue, changedPasswordValue);

// Logout again, login with original account
await mainMenu.logout();
let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`];
if(!emailInputValue) {
throw new Error("MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine} and/or MAGENTO_EXISTING_ACCOUNT_PASSWORD have not defined in the .env file, or the account hasn't been created yet.");
}
await loginPage.login(emailInputValue, passwordInputValue);
});
});


// TODO: Add tests to check address can't be added/updated if the supplied information is incorrect
// TODO: Add tests to check address can't be deleted if it's the last/only one.
test.describe('Account address book actions', { annotation: {type: 'Account Dashboard', description: 'Tests for the Address Book'},}, () => {
test.describe.serial('Account address book actions', { annotation: {type: 'Account Dashboard', description: 'Tests for the Address Book'},}, () => {
test.beforeEach(async ({page}) => {
// go to the Adress Book page
await page.goto(slugs.account.addressBookSlug);
await page.waitForLoadState();
});
Expand All @@ -91,11 +107,14 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash
*/

test('I can add my first address',{ tag: '@address-actions', }, async ({page}, testInfo) => {
// If account has no address, Address Book redirects to the 'Add New Address' page.
// We expect this to be true before continuing.
let addNewAddressTitle = page.getByRole('heading', {level: 1, name: selectors.newAddress.addNewAddressTitle});
testInfo.skip(await addNewAddressTitle.isHidden(), `Heading "Add New Addres" is not found, please check if an address has already been added.`);
const accountPage = new AccountPage(page);
let addNewAddressTitle = page.getByRole('heading', {level: 1, name: selectors.newAddress.addNewAddressTitle});

if(await addNewAddressTitle.isHidden()) {
await accountPage.deleteAllAddresses();
testInfo.annotations.push({ type: 'Notification: deleted addresses', description: `All addresses are deleted to recreate the first address flow.` });
await page.goto(slugs.account.addressNewSlug);
}

await accountPage.addNewAddress();
});
Expand All @@ -112,6 +131,7 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash
test('I can add another address',{ tag: '@address-actions', }, async ({page}) => {
await page.goto(slugs.account.addressNewSlug);
const accountPage = new AccountPage(page);

await accountPage.addNewAddress();
});

Expand Down Expand Up @@ -156,21 +176,20 @@ test.describe('Account address book actions', { annotation: {type: 'Account Dash
const accountPage = new AccountPage(page);

let deleteAddressButton = page.getByRole('link', {name: selectors.accountDashboard.addressDeleteIconButton}).first();
testInfo.skip(await deleteAddressButton.isHidden(), `Button to delete Address is not found, please check if an address has been added.`);

if(await deleteAddressButton.isHidden()) {
await page.goto(slugs.account.addressNewSlug);
await accountPage.addNewAddress(inputvalues.firstAddress.firstPhoneNumberValue, inputvalues.firstAddress.firstStreetAddressValue, inputvalues.firstAddress.firstZipCodeValue, inputvalues.firstAddress.firstCityValue, inputvalues.firstAddress.firstProvinceValue);
}
await accountPage.deleteFirstAddressFromAddressBook();
});
});

// TODO: move this to new spec file.
test.describe('Newsletter actions', { annotation: {type: 'Account Dashboard', description: 'Newsletter tests'},}, () => {
test.beforeEach(async ({page}) => {
// go to the Dashboard page
await page.goto(slugs.account.accountOverviewSlug);
});

// TODO: What if website offers multiple subscriptions?

/**
* @feature Magento 2 newsletter subscriptions
* @scenario User (un)subscribes from a newsletter
Expand Down Expand Up @@ -201,6 +220,4 @@ test.describe('Newsletter actions', { annotation: {type: 'Account Dashboard', de
await expect(newsletterCheckElement).not.toBeChecked();
}
});

});

2 changes: 0 additions & 2 deletions tests/base/cart.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ test.describe('Cart functionalities (guest)', () => {
const mainMenu = new MainMenuPage(page);
const productPage = new ProductPage(page);

//TODO: Use a storagestate or API call to add product to the cart so shorten test time
await page.goto(slugs.productpage.simpleProductSlug);
await productPage.addSimpleProductToCart(selectors.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug);
await mainMenu.openMiniCart();
Expand Down Expand Up @@ -127,7 +126,6 @@ test.describe('Cart functionalities (guest)', () => {
throw new Error(`MAGENTO_COUPON_CODE_${browserEngine} appears to not be set in .env file. Value reported: ${discountCode}`);
}

// TODO: create API call to quickly add discount code rather than run a test again.
await cart.applyDiscountCode(discountCode);
await cart.removeDiscountCode();
});
Expand Down
9 changes: 0 additions & 9 deletions tests/base/checkout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import inputvalues from './config/input-values/input-values.json';
import selectors from './config/selectors/selectors.json';


// no resetting storageState, mainmenu has more functionalities when logged in.

/**
* @feature BeforeEach runs before each test in this group.
* @scenario Add product to the cart, confirm it's there, then move to checkout.
Expand All @@ -25,7 +23,6 @@ import selectors from './config/selectors/selectors.json';
test.beforeEach(async ({ page }) => {
const productPage = new ProductPage(page);

//TODO: Use a storagestate or API call to add product to the cart so shorten test time
await page.goto(slugs.productpage.simpleProductSlug);
await productPage.addSimpleProductToCart(selectors.productPage.simpleProductTitle, slugs.productpage.simpleProductSlug);
await page.goto(slugs.checkoutSlug);
Expand All @@ -34,7 +31,6 @@ test.beforeEach(async ({ page }) => {

test.describe('Checkout (login required)', () => {
// Before each test, log in
// TODO: remove this beforeEach() once authentication as project set-up/fixture works.
test.beforeEach(async ({ page, browserName }) => {
const browserEngine = browserName?.toUpperCase() || "UNKNOWN";
let emailInputValue = process.env[`MAGENTO_EXISTING_ACCOUNT_EMAIL_${browserEngine}`];
Expand Down Expand Up @@ -105,9 +101,6 @@ test.describe('Checkout (login required)', () => {
});

test.describe('Checkout (guest)', () => {
// TODO: Write test to confirm order can be placed without an account
// TODO: Write test for logged-in user who hasn't added an address yet.

/**
* @feature Discount Code
* @scenario User adds a discount code to their cart
Expand All @@ -121,7 +114,6 @@ test.describe('Checkout (guest)', () => {
* @and a discount should be applied to the product
*/
test('Add coupon code in checkout',{ tag: ['@checkout', '@coupon-code']}, async ({page, browserName}) => {
//TODO: Write tests to ensure code also works if user is NOT logged in.
const checkout = new CheckoutPage(page);
const browserEngine = browserName?.toUpperCase() || "UNKNOWN";
let discountCode = process.env[`MAGENTO_COUPON_CODE_${browserEngine}`];
Expand Down Expand Up @@ -156,7 +148,6 @@ test.describe('Checkout (guest)', () => {
throw new Error(`MAGENTO_COUPON_CODE appears to not be set in .env file. Value reported: ${discountCode}`);
}

// TODO: create API call to quickly add discount code rather than run a test again.
await checkout.applyDiscountCodeCheckout(discountCode);
await checkout.removeDiscountCode();
});
Expand Down
5 changes: 4 additions & 1 deletion tests/base/config/selectors/selectors.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
"nameFieldLabel": "Name",
"emailFieldLabel": "Email",
"passwordFieldLabel": "Password",
"newPasswordFieldLabel": "New Password",
"passwordConfirmFieldLabel": "Confirm Password",
"loginButtonLabel": "Sign In"
"newPasswordConfirmFieldLabel": "Confirm New Password",
"loginButtonLabel": "Sign In",
"currentPasswordFieldLabel": "Current Password"
},
"personalInformation": {
"firstNameLabel": "First Name",
Expand Down
3 changes: 2 additions & 1 deletion tests/base/config/slugs.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"createAccountSlug": "/customer/account/create",
"addressBookSlug": "/customer/address",
"addressIndexSlug": "/customer/address/index",
"addressNewSlug": "customer/address/new"
"addressNewSlug": "customer/address/new",
"changePasswordSlug": "/customer/account/edit/changepass/1/"
},
"productpage": {
"simpleProductSlug": "/push-it-messenger-bag.html",
Expand Down
42 changes: 21 additions & 21 deletions tests/base/fixtures/account.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,7 @@ export class AccountPage {
readonly accountCreationPasswordRepeatField: Locator;
readonly accountCreationConfirmButton: Locator;

// fields below are not required when adding an address.
/*
readonly companyField: Locator;
readonly streetAddressFieldTwo: Locator;
readonly streetAddressFieldThree: Locator;
*/

// TODO: Update these functionalities to be able to take in non-required fields.


constructor(page: Page){
this.page = page;
this.accountDashboardTitle = page.getByRole('heading', { name: selectors.accountDashboard.accountDashboardTitleLabel });
Expand All @@ -50,20 +42,15 @@ export class AccountPage {
this.streetAddressField = page.getByLabel(selectors.newAddress.streetAddressLabel, {exact:true});
this.zipCodeField = page.getByLabel(selectors.newAddress.zipCodeLabel);
this.cityField = page.getByLabel(selectors.newAddress.cityNameLabel);
//TODO: countrySelect is used to change the country so it's not US.
//TODO: provinceSelect should be provinceField if country is, for example, Netherlands.
this.countrySelectorField = page.getByLabel(selectors.newAddress.countryLabel);
this.stateSelectorField = page.getByLabel(selectors.newAddress.provinceSelectLabel).filter({hasText: selectors.newAddress.provinceSelectFilterLabel});
this.saveAddressButton = page.getByRole('button',{name: selectors.newAddress.saveAdressButton});
//unrequired fields
// this.companyField = page.getByLabel(selectors.newAddress.companyNameLabel);

// Account Information elements
this.changePasswordCheck = page.getByRole('checkbox', {name: selectors.personalInformation.changePasswordCheckLabel});
//TODO: Fix these once I can log in again
this.currentPasswordField = page.getByLabel('Current Password');
this.newPasswordField = page.getByLabel('New Password', {exact:true});
this.confirmNewPasswordField = page.getByLabel('Confirm New Password')
this.currentPasswordField = page.getByLabel(selectors.credentials.currentPasswordFieldLabel);
this.newPasswordField = page.getByLabel(selectors.credentials.newPasswordFieldLabel, {exact:true});
this.confirmNewPasswordField = page.getByLabel(selectors.credentials.newPasswordConfirmFieldLabel);
this.genericSaveButton = page.getByRole('button', { name: selectors.general.genericSaveButtonLabel });

// Account Creation elements
Expand Down Expand Up @@ -127,8 +114,6 @@ export class AccountPage {
}


// TODO: Update function to remove random address from address book?
// deleteAddressButton is currently the first instance it finds.
async deleteFirstAddressFromAddressBook(){
let addressDeletedNotification = verify.address.addressDeletedNotification;
// Dialog function to click confirm
Expand All @@ -138,7 +123,6 @@ export class AccountPage {
}
});

// .click() replaced by .press("Enter") as a workaround for webkit issues
await this.deleteAddressButton.click();
await this.page.waitForLoadState();
await expect(this.page.getByText(addressDeletedNotification)).toBeVisible();
Expand All @@ -152,7 +136,6 @@ export class AccountPage {
await this.newPasswordField.fill(newPassword);
await this.confirmNewPasswordField.fill(newPassword);

// .click() replaced by .press("Enter") as a workaround for webkit issues
await this.genericSaveButton.click();
await this.page.waitForLoadState();

Expand All @@ -171,4 +154,21 @@ export class AccountPage {
await this.accountCreationPasswordRepeatField.fill(password);
await this.accountCreationConfirmButton.click();
}

async deleteAllAddresses() {
let addressDeletedNotification = verify.address.addressDeletedNotification;

this.page.on('dialog', async (dialog) => {
if (dialog.type() === 'confirm') {
await dialog.accept();
}
});

while (await this.deleteAddressButton.isVisible()) {
await this.deleteAddressButton.click();
await this.page.waitForLoadState();

await expect(this.page.getByText(addressDeletedNotification)).toBeVisible();
}
}
}
1 change: 0 additions & 1 deletion tests/base/fixtures/checkout.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import slugs from '../config/slugs.json';

export class CheckoutPage {

//TODO: Expand with fields for when user is not logged in or has not provided an address
readonly page: Page;
readonly shippingMethodOptionFixed: Locator;
readonly paymentMethodOptionCheck: Locator;
Expand Down
4 changes: 0 additions & 4 deletions tests/base/fixtures/login.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,13 @@ export class LoginPage {
this.loginButton = page.getByRole('button', { name: selectors.credentials.loginButtonLabel });
}

// Note: this login function is simply written to quickly log in for tests which require you to be logged in.
// TODO: this login function should be moved to an auth file. see login.spec.ts line 15 and 16 for more info.
async login(email: string, password: string){
await this.page.goto(slugs.account.loginSlug);
await this.loginEmailField.fill(email);
await this.loginPasswordField.fill(password);
// usage of .press("Enter") to prevent webkit issues with button.click();
await this.loginButton.press("Enter");

// this element cannot be defined in the constructor, since the sign out button only appears after logging in.
// Using a selector rather than expected, since it's we're locating a button rather than an expected notification.
await expect(this.page.getByRole('link', { name: selectors.mainMenu.myAccountLogoutItem })).toBeVisible();
}
}
2 changes: 1 addition & 1 deletion tests/base/fixtures/mainmenu.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class MainMenuPage {
}

async gotoAddressBook() {
// TODO: create function to navigate to Address Book through the header menu links
// create function to navigate to Address Book through the header menu links
}

async openMiniCart() {
Expand Down
2 changes: 0 additions & 2 deletions tests/base/fixtures/newsletter.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ export class NewsletterSubscriptionPage {
this.saveSubscriptionsButton = page.getByRole('button', {name:selectors.newsletterSubscriptions.saveSubscriptionsButton});
}

//TODO: the text of the notification can differ sometimes. Update this function to take this into account.
// newsletterRemovedNotification should sometimes be newsletterUpdatedNotification.
async updateNewsletterSubscription(){

if(await this.newsletterCheckElement.isChecked()) {
Expand Down
Loading

0 comments on commit a050f9b

Please sign in to comment.