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

Gleb avrorin #6

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 51 additions & 0 deletions .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Cypress Tests

on:
push:
pull_request:
workflow_dispatch:

jobs:
cypress-e2e:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install dependencies
run: npm install

- name: Run e2e Tests
run: npx cypress run --spec 'cypress/e2e/' --reporter mochawesome --reporter-options "reportDir=cypress/reports,overwrite=false,html=false,json=true"

- name: Upload Mochawesome Reports as Artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: mochawesome-reports
path: cypress/reports/*.json

- name: Download Mochawesome Reports Artifact
uses: actions/download-artifact@v4
with:
name: mochawesome-reports
path: ./downloaded-reports

- name: Zip the downloaded reports
run: |
zip -r ./downloaded-reports/mochawesome-reports.zip ./downloaded-reports

- name: Upload ZIP file to Uploadcare
env:
UPLOADCARE_PUB_KEY: ${{ secrets.UPLOADCARE_PUBLIC_KEY }}
run: |
curl -F "UPLOADCARE_PUB_KEY=${UPLOADCARE_PUB_KEY}" \
-F "file=@./downloaded-reports/mochawesome-reports.zip" \
-F "UPLOADCARE_STORE=auto" \
"https://upload.uploadcare.com/base/"
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Environment variables
.env

# Cypress screenshots and videos
cypress/screenshots/
cypress/videos/

# Cypress reports
cypress/reports/

# Node.js dependencies
node_modules/

# Miscellaneous
.DS_Store
*.log
*.tmp
53 changes: 53 additions & 0 deletions backup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Cypress Tests

on:
push:
pull_request:
workflow_dispatch:

jobs:
cypress-e2e:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install dependencies
run: npm install

- name: Run e2e Tests
run: npx cypress run --spec 'cypress/e2e/' --reporter mochawesome --reporter-options "reportDir=cypress/reports,overwrite=false,html=false,json=true"

- name: Upload Cypress screenshots
uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-screenshots-profile
path: cypress/screenshots


compress:
runs-on: ubuntu-latest
needs: [cypress-e2e]
if: always()
steps:
- name: List files in cypress directory (compress job)
run: ls -R cypress
- name: Compress the MochaAwesome report
run: |
mkdir -p cypress-reports
cp -r cypress/reports/* cypress-reports/
zip -r cypress-reports.zip cypress-reports

- name: Upload file to Uploadcare
run: |
curl -F "UPLOADCARE_PUB_KEY=1e9a01d70860ee491ef0" \
-F "file=@cypress-reports.zip" \
-F "UPLOADCARE_STORE=auto" \
"https://upload.uploadcare.com/base/"
23 changes: 23 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const { defineConfig } = require("cypress");

module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
reporter: 'mochawesome',
reporterOptions: {
reportDir: 'cypress/reports',
overwrite: false,
html: false,
json: true
},
baseUrl: "https://www.crexi.com/",
defaultCommandTimeout: 20000,
// retries: 3,
viewportWidth: 1920,
viewportHeight: 1080,
screenshotOnRunFailure: true,
screenshotsFolder: "cypress/screenshots",
},
});
57 changes: 57 additions & 0 deletions cypress/e2e/homePage/profilePage.spec.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import "cypress-real-events/support";
import mainPage from "../../page_objects/main.page";
import dashboardPage from "../../page_objects/dashboard.page";

describe("Profile", () => {
beforeEach(() => {
cy.clearCookies();
cy.clearLocalStorage();
cy.intercept({ resourceType: /xhr|fetch/ }, { log: false });
cy.errorHandler();
cy.loginByApi();
});

it("Should change Profile Photo with Valid PNG format", () => {
cy.intercept("PATCH", "https://api.crexi.com/account").as("avatarUpdated");
mainPage.userIcon.realHover();
cy.contains(" Account Settings ").click({ force: true });
mainPage.accountSettingsLink.click();
cy.url().should("eq", "https://www.crexi.com/dashboard/profile");
dashboardPage.editPersonalDataButton.click({ force: true });
//check that it accepts only images
dashboardPage.editAvatarButton.eq(0).should("have.attr", "accept", "image/*");
dashboardPage.editAvatarButton.eq(0).selectFile(`cypress/fixtures/gnome_paladin.png`);
cy.contains("button", "Update").click();
//waiting for API request that verifies taht PATCH requerst has been complete
cy.wait("@avatarUpdated");
cy.contains("Your personal info has been updated.").should("be.visible");
});

it("Should change Profile Photo with Valid JPG format", () => {
cy.intercept("PATCH", "https://api.crexi.com/account").as("avatarUpdated");
mainPage.userIcon.realHover();
cy.contains(" Account Settings ").click({ force: true });
mainPage.accountSettingsLink.click();
cy.url().should("eq", "https://www.crexi.com/dashboard/profile");
dashboardPage.editPersonalDataButton.click({ force: true });
//check that it accepts only images
dashboardPage.editAvatarButton.eq(0).should("have.attr", "accept", "image/*");
dashboardPage.editAvatarButton.eq(0).selectFile(`cypress/fixtures/City_view.jpg`);
cy.contains("button", "Update").click();
//waiting for API request that verifies taht PATCH requerst has been complete
cy.wait("@avatarUpdated");
cy.contains("Your personal info has been updated.").should("be.visible");
});

it.skip("Should throw error if using wrong format (MP4)", () => {
cy.intercept("PATCH", "https://api.crexi.com/account").as("avatarUpdated");
cy.visit("/dashboard/profile");
dashboardPage.editPersonalDataButton.click({ force: true });
//check that it accepts only images
dashboardPage.editAvatarButton.eq(0).selectFile(`cypress/fixtures/videofile.mp4`);
cy.contains("button", "Update").click();
cy.wait("@avatarUpdated");
//should be some error that would let us know that we're using wrong format
cy.contains("Some error message").should("be.visible");
});
});
90 changes: 90 additions & 0 deletions cypress/e2e/homePage/registrationPage.spec.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import registrationForm from "../../page_objects/signInForm.page";
import mainPage from "../../page_objects/main.page";
import errorMessage from "../../fixtures/verification.json";
import verification from "../../fixtures/credentials.json";

describe("Registration", () => {
beforeEach(() => {
cy.intercept({ resourceType: /xhr|fetch/ }, { log: false });
cy.clearCookies();
cy.clearLocalStorage();
cy.errorHandler();
cy.visit("/");
});

it("Should Sign Up using valid data as Owner/Property Manager", () => {
//we can use 3 way of passing data:
// 1.Writing them manually, 2.Using fakerjs 3.Putting them within fixtures file
// since the test doesnt allow me to register user (see line 28 comment), i would simply hardcode it
//My usual approach is to user faker
mainPage.signupAndLoginButton.click();
registrationForm.firstNameInputField.type("TestName");
registrationForm.lastNameInputField.type("TestLastName");
registrationForm.emailInputField.type("email@email.com");
registrationForm.passwordInputField.type("123password123");
registrationForm.roleDropDownMenu.click();
cy.contains("Owner/Property Manager").click();
registrationForm.phoneInputField.type("7471112233");
//i cannot complete these step, since there might be some bot that blocks creation via cypress/framework.
});
//since i cannot use this in prod, i would just state what test i would have done if that would be staging env:
// it("User is able to sign up for an account as a Listing Broker/Agent", () => {})
// it("User is able to sign up for an account as a Listing Broker/Agent", () => {})
// it("User is able to sign up for an account as a Selling/Buying Broker/Agent",() => {})
// it("User is able to sign up for an account as a Transaction Coordinator",() => {})
// it("User is able to sign up for an account as a Landlord Broker/Agent",() => {})
// it("User is able to sign up for an account as a Tenant Rep Broker",() => {})
// it("User is able to sign up for an account as a Principal",() => {})
// it("User is able to sign up for an account as a Lender",() => {})
// it("User is able to sign up for an account as a Assessor",() => {})
// it("User is able to sign up for an account as a Appraiser",() => {})
// it("User is able to sign up for an account as a Third Party Service",() => {})
// it("User is able to sign up for an account as a Tenant",() => {})
// it("User is able to sign up for an account as a Owner/Property Manager",() => {})
// it("User is able to sign up for an account as a Other",() => {})

it("Should be able to Login with existing/created account and Logout", () => {
mainPage.signupAndLoginButton.click();
registrationForm.logInTab.click();
registrationForm.emailInputField.type(verification.brokerUser.email);
registrationForm.passwordInputField.type(verification.brokerUser.password);
registrationForm.loginButton.click();
cy.get('[data-cy="authorizationForm"]').should("not.exist");
registrationForm.signOut()
});

it("Should see error messages when signing up with empty data", () => {
mainPage.signupAndLoginButton.click();
registrationForm.signUpButton.click();
cy.wait(1000);
registrationForm.firstNameErrorMessage.should("be.visible").and("have.text", errorMessage.missedFirstName);
registrationForm.lastNameErrorMessage.should("be.visible").and("have.text", errorMessage.missedLastName);
registrationForm.emailErrorMessage.should("be.visible").and("have.text", errorMessage.missedEmail);
registrationForm.passwordErrorMessage.should("be.visible").and("have.text", errorMessage.missedPassword);
registrationForm.roleErrorMessage.should("be.visible").and("have.text", errorMessage.missedRole);
});

it("Should Not sign up with invalid email format(no '@' symbol)", () => {
mainPage.signupAndLoginButton.click();
cy.wait(1500);
registrationForm.emailInputField.type(verification.invalidEmailPattern);
registrationForm.signUpButton.click();
registrationForm.emailErrorMessage.should("be.visible").and("have.text", errorMessage.invalidEmail);
});

it("Should Not sign up with invalid amount of password chars", () => {
mainPage.signupAndLoginButton.click();
registrationForm.passwordInputField.should("be.visible").type("12345678901");
registrationForm.signUpButton.click();
registrationForm.passwordErrorMessage.should("be.visible").and("have.text", errorMessage.passwordMinLengthError);
});

it("Should Not Log in with invalid user data", () => {
mainPage.signupAndLoginButton.click();
registrationForm.logInTab.click();
registrationForm.emailInputField.type(verification.invalidUser.invalidEmail);
registrationForm.passwordInputField.type(verification.invalidUser.invalidPassword);
registrationForm.loginButton.click();
registrationForm.incorrectUserErrorMessage.should("be.visible").and("have.text", errorMessage.loginInvalidData);
});
});
76 changes: 76 additions & 0 deletions cypress/e2e/homePage/searchPage.spec.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import mainPage from "../../page_objects/main.page";
import listingPage from "../../page_objects/listing.page";

describe("Search", () => {
beforeEach(() => {
cy.intercept({ resourceType: /xhr|fetch/ }, { log: false });
cy.errorHandler();
cy.visit("/");
});

it("Should search by Keyword", () => {
mainPage.searchBar.type("test", { force: true });
cy.wait(1500)
mainPage.searchButton.click();
cy.url().should("contain", "?sort=Relevance&term=test");
listingPage.listingElements.should("be.visible");
});

it("Should search by City using helper", () => {
cy.intercept('POST', 'https://api.crexi.com/assets/search').as('searchResults')
mainPage.searchBar.type("Los");
// cy.contains("Los Angeles").click();
cy.get('[data-cy="searchSuggestionItem"] [title="Los Angeles, CA"]').click();
cy.get("h1").should("contain", " Los Angeles, CA Properties for Sale ");
cy.wait('@searchResults')
cy.get('[class="search-bar-pill-text"]').should("have.text", "Los Angeles, CA");
listingPage.listingElements.should("be.visible");
});

it("Should turn the map on/off", () => {
cy.visit("properties?placeIds%5B%5D=ChIJE9on3F3HwoAR9AhGJW_fL-I&mapZoom=9");
cy.wait(3000);
listingPage.mapButton.should("have.attr", "aria-checked", "true");
listingPage.googleMapElement.should("have.attr", "class", "ng-star-inserted");
listingPage.mapButton.click();
listingPage.mapButton.should("have.attr", "aria-checked", "false");
cy.url().should("contain", "showMap=false");
listingPage.googleMapElement.should("have.attr", "class", "ng-star-inserted hide-map-view");
});

it("Should search by Property type", () => {
mainPage.propertyTypeDropDown.click();
mainPage.allCheckBoxAtPropertyType.click();
cy.contains(" Senior Living ").click();
mainPage.searchButton.click();
cy.url().should("contain", "properties?types%5B%5D=Senior%20Living");
});

it("Should filter search by Price", () => {
// cy.intercept("POST", "https://api.crexi.com/assets/search").as("stableDom");
const minValue = 1000000;
const maxValue = 1200000;
cy.visit("/properties");
listingPage.priceFilter.click();
listingPage.minPriceInput.type(minValue);
listingPage.maxPriceInput.type(maxValue);
listingPage.excludeUnpricedCheckbox.check();
listingPage.applyButton.click();
listingPage.closePopUp.click();
cy.url().should("contain", `askingPriceMax=${maxValue}&askingPriceMin=${minValue}`);
cy.wait(2000);
// cy.wait("@stableDom").then((interception) => {
// console.log(interception);
// });
const elementsToCheck = [0, 1, 20, 58, 59];
elementsToCheck.forEach((index) => {
cy.get('[data-cy="propertyPrice"]')
.eq(index)
.invoke("text")
.then((text) => {
const price = Number(text.replace(/[$,]/g, ""));
cy.wrap(price).should("be.within", minValue, maxValue, `Element at index ${index} has a price of ${text}`);
});
});
});
});
Binary file added cypress/fixtures/City_view.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions cypress/fixtures/credentials.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"brokerUser": {
"email": "something@google.com",
"password": "123123123123",
"brokerRole": "Broker/Agent"
},

"invalidUser": {
"invalidEmail": "somestupidemail@test.com",
"invalidPassword": "invalidPassword!"
},
"invalidEmailPattern": "coolerzamdgmail.com",
"firstName": "Gleb",
"lastName": "Cypress"

}
Binary file added cypress/fixtures/gnome_paladin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added cypress/fixtures/stormwind-city.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading