From a4fbb3c14f8843428e017dbdeed95c7292718f33 Mon Sep 17 00:00:00 2001 From: Yuliia Dovbysh Date: Thu, 7 Nov 2024 13:57:58 +0200 Subject: [PATCH] Solution --- cypress/e2e/article.cy.js | 97 ++++++++++++----- cypress/e2e/settings.cy.js | 81 ++++++++------ cypress/e2e/signIn.cy.js | 75 +++++++------ cypress/e2e/signUp.cy.js | 57 +++++++--- cypress/e2e/user.cy.js | 66 +++++++++--- cypress/support/PageObject.js | 14 +-- cypress/support/commands.js | 107 ++++++++++++------- cypress/support/pages/article.pageObject.js | 87 +++++++++++++++ cypress/support/pages/home.pageObject.js | 31 +++--- cypress/support/pages/settings.PageObject.js | 63 +++++++++++ cypress/support/pages/signIn.pageObject.js | 80 ++++++++------ cypress/support/pages/signUp.PageObject.js | 55 ++++++++++ cypress/support/pages/user.PageObject.js | 33 ++++++ 13 files changed, 634 insertions(+), 212 deletions(-) create mode 100644 cypress/support/pages/article.pageObject.js create mode 100644 cypress/support/pages/settings.PageObject.js create mode 100644 cypress/support/pages/signUp.PageObject.js create mode 100644 cypress/support/pages/user.PageObject.js diff --git a/cypress/e2e/article.cy.js b/cypress/e2e/article.cy.js index e1ef839b..66eca6e7 100644 --- a/cypress/e2e/article.cy.js +++ b/cypress/e2e/article.cy.js @@ -1,24 +1,73 @@ -/// -/// - -describe('Article', () => { - before(() => { - - }); - - beforeEach(() => { - cy.task('db:clear'); - }); - - it('should be created using New Article form', () => { - - }); - - it('should be edited using Edit button', () => { - - }); - - it('should be deleted using Delete button', () => { - - }); -}); +/// +/// + +import { faker } from "@faker-js/faker"; + +import ArticlePageObject from "../support/pages/article.pageObject"; +const articlePage = new ArticlePageObject(); + +describe("Article", () => { + let user; + let article; + let newArticle; + + before(() => { + cy.task("generateUser").then((generateUser) => { + user = generateUser; + }); + cy.task("generateArticle").then((generatedArticle) => { + article = generatedArticle; + newArticle = { + title: "Edited " + generatedArticle.title, + description: "Edited " + generatedArticle.description, + body: "Edited " + generatedArticle.body, + tag: "edited-tag", + }; + }); + }); + + beforeEach(() => { + cy.task("db:clear"); + cy.visit("/"); + cy.registerAndLogin(user.email, user.username, user.password); + articlePage.visit(); + }); + + it("should be created using New Article form", () => { + articlePage.typeTitle(article.title); + articlePage.typeDescription(article.description); + articlePage.typeBodyArticle(article.body); + articlePage.typeTag(article.tag); + articlePage.clickPublishBtn(); + articlePage.assertArticlePage(article.title); + }); + + it("should be edited using Edit button", () => { + cy.createArticle( + article.title, + article.description, + article.body, + article.tag + ); + articlePage.assertArticlePage(article.title); + articlePage.clickEditArticleBtn(); + articlePage.typeNewTitle(newArticle.title); + articlePage.typeNewDescription(newArticle.description); + articlePage.typeNewBodyArticle(newArticle.body); + articlePage.typeTag(newArticle.tag); + articlePage.clickPublishBtn(); + articlePage.assertTitleArticlePage(newArticle.title); + }); + + it("should be deleted using Delete button", () => { + cy.createArticle( + article.title, + article.description, + article.body, + article.tag + ); + articlePage.assertArticlePage(article.title); + articlePage.clickDeleteArticleBtn(); + cy.url().should("not.include", article.title); + }); +}); diff --git a/cypress/e2e/settings.cy.js b/cypress/e2e/settings.cy.js index e9208789..31d7256a 100644 --- a/cypress/e2e/settings.cy.js +++ b/cypress/e2e/settings.cy.js @@ -1,32 +1,49 @@ -/// -/// - -describe('Settings page', () => { - before(() => { - - }); - - beforeEach(() => { - - }); - - it('should provide an ability to update username', () => { - - }); - - it('should provide an ability to update bio', () => { - - }); - - it('should provide an ability to update an email', () => { - - }); - - it('should provide an ability to update password', () => { - - }); - - it('should provide an ability to log out', () => { - - }); -}); +/// +/// +import SettingsPageObject from "../support/pages/settings.PageObject"; +import HomePageObject from "../support/pages/home.pageObject"; + +const settingsPage = new SettingsPageObject(); +const homePage = new HomePageObject(); + +describe("Settings page", () => { + let user; + before(() => { + cy.task("generateUser").then((generateUser) => { + user = generateUser; + }); + }); + + beforeEach(() => { + cy.task("db:clear"); + cy.visit("/#/register"); + cy.registerAndLogin(user.email, user.username, user.password); + settingsPage.visit(); + }); + + it("should provide an ability to update username", () => { + settingsPage.typeUsername(user.username); + settingsPage.clickUpdateSettingsBtn(); + homePage.assertHeaderContainUsername(user.username); + }); + + it("should provide an ability to update bio", () => { + settingsPage.typeBio(user.bio); + settingsPage.clickUpdateSettingsBtn(); + }); + + it("should provide an ability to update an email", () => { + settingsPage.typeEmail(user.email); + settingsPage.clickUpdateSettingsBtn(); + }); + + it("should provide an ability to update password", () => { + settingsPage.typePassword(user.password); + settingsPage.clickUpdateSettingsBtn(); + }); + + it("should provide an ability to log out", () => { + settingsPage.clickLogoutBtn(); + settingsPage.assertLogout("/#/"); + }); +}); diff --git a/cypress/e2e/signIn.cy.js b/cypress/e2e/signIn.cy.js index 3ae60e04..1f8983ae 100644 --- a/cypress/e2e/signIn.cy.js +++ b/cypress/e2e/signIn.cy.js @@ -1,34 +1,41 @@ -/// -/// - -import SignInPageObject from '../support/pages/signIn.pageObject'; -import HomePageObject from '../support/pages/home.pageObject'; - -const signInPage = new SignInPageObject(); -const homePage = new HomePageObject(); - -describe('Sign In page', () => { - let user; - - before(() => { - cy.task('db:clear'); - cy.task('generateUser').then((generateUser) => { - user = generateUser; - }); - }); - - it('should provide an ability to log in with existing credentials', () => { - signInPage.visit(); - cy.register(user.email, user.username, user.password); - - signInPage.typeEmail(user.email); - signInPage.typePassword(user.password); - signInPage.clickSignInBtn(); - - homePage.assertHeaderContainUsername(user.username); - }); - - it('should not provide an ability to log in with wrong credentials', () => { - - }); -}); +/// +/// + +import { faker } from "@faker-js/faker"; + +import SignInPageObject from "../support/pages/signIn.pageObject"; +import HomePageObject from "../support/pages/home.pageObject"; + +const signInPage = new SignInPageObject(); +const homePage = new HomePageObject(); + +describe("Sign In page", () => { + let user; + + beforeEach(() => { + cy.task("db:clear"); + cy.task("generateUser").then((generateUser) => { + user = generateUser; + }); + }); + + it("should provide an ability to log in with existing credentials", () => { + signInPage.visit(); + cy.register(user.email, user.username, user.password); + + signInPage.typeEmail(user.email); + signInPage.typePassword(user.password); + signInPage.clickSignInBtn(); + + homePage.assertHeaderContainUsername(user.username); + }); + + it("should not provide an ability to log in with wrong credentials", () => { + signInPage.visit(); + signInPage.typeEmail(user.email); + signInPage.typePassword(user.password + "123"); + signInPage.clickSignInBtn(); + signInPage.assertErrorMessage(); + signInPage.assertLoginPage(); + }); +}); diff --git a/cypress/e2e/signUp.cy.js b/cypress/e2e/signUp.cy.js index ccf57970..87d2d436 100644 --- a/cypress/e2e/signUp.cy.js +++ b/cypress/e2e/signUp.cy.js @@ -1,12 +1,45 @@ -/// -/// - -describe('Sign Up page', () => { - before(() => { - - }); - - it('should ...', () => { - - }); -}); +/// +/// + +import SignUpPageObject from "../support/pages/signUp.PageObject"; +import HomePageObject from "../support/pages/home.pageObject"; + +const signUpPage = new SignUpPageObject(); +const homePage = new HomePageObject(); + +describe("Sign Up page", () => { + let user; + let newUser; + beforeEach(() => { + cy.task("db:clear"); + cy.task("generateUser").then((generatedUser) => { + user = generatedUser; + }); + cy.task("generateUser").then((generatedUser) => { + newUser = generatedUser; + }); + signUpPage.visit(); + }); + + it("should provide an ability to sign up with valid credentials", () => { + signUpPage.visit(); + cy.getByDataCy("signup-username-field").should("be.visible"); + signUpPage.typeUsername(user.username); + signUpPage.typeEmail(user.email); + signUpPage.typePassword(user.password); + signUpPage.clickSignUpBtn(); + signUpPage.assertSuccessfulMessage(); + + homePage.assertHeaderContainUsername(user.username); + }); + + it("should not provide an ability to sign up with existing email", () => { + cy.register(user.email, user.username, user.password); + signUpPage.typeUsername(newUser.username); + signUpPage.typeEmail(user.email); + signUpPage.typePassword(newUser.password); + signUpPage.clickSignUpBtn(); + signUpPage.assertErrorMessage(); + signUpPage.assertSignUpPage(); + }); +}); diff --git a/cypress/e2e/user.cy.js b/cypress/e2e/user.cy.js index 012f6fa7..477a6691 100644 --- a/cypress/e2e/user.cy.js +++ b/cypress/e2e/user.cy.js @@ -1,12 +1,54 @@ -/// -/// - -describe('User', () => { - before(() => { - - }); - - it.skip('should be able to follow the another user', () => { - - }); -}); +/// +/// + +import HomePageObject from '../support/pages/home.pageObject'; +import SignInPageObject from '../support/pages/signIn.pageObject'; +import UserPageObject from "../support/pages/user.PageObject"; +const signInPage = new SignInPageObject(); +const userPage = new UserPageObject(); +const homePage = new HomePageObject(); + +describe("User", () => { + let user; + let secondUser; + + before(() => { + cy.task('db:clear'); + // Generating first user + cy.task('generateUser').then((generatedUser) => { + user = generatedUser; + // Registering first user + cy.register(user.email, user.username, user.password); + }); + + // Generating second user + cy.task('generateUser').then((generatedUser) => { + secondUser = generatedUser; + // Registering second user + cy.register(secondUser.email, secondUser.username, secondUser.password); + }); + }); + + beforeEach(() => { + signInPage.visit(); + signInPage.typeEmail(user.email); + signInPage.typePassword(user.password); + signInPage.clickSignInBtn(); + homePage.assertHeaderContainUsername(user.username); + }); + + it("should allow following a user", () => { + userPage.visitUserPage(secondUser.username); + userPage.assertCanFollowUser(); + userPage.clickFollowBtn(); + userPage.assertFollowingUser(secondUser.username); + }); + + it('should allow unfollowing a user', () => { + userPage.visitUserPage(secondUser.username); + userPage.assertFollowingUser(secondUser.username); + userPage.clickUnfollowBtn(); + userPage.assertCanFollowUser(); + }); +}); + diff --git a/cypress/support/PageObject.js b/cypress/support/PageObject.js index 231b1a49..3e3d5ac1 100644 --- a/cypress/support/PageObject.js +++ b/cypress/support/PageObject.js @@ -1,7 +1,7 @@ -class PageObject { - visit(url) { - cy.visit(url || this.url); - } -} - -export default PageObject; +class PageObject { + visit(url) { + cy.visit(url || this.url); + } +} + +export default PageObject; diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 40634fe9..5c757a39 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,41 +1,66 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) - -import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command'; - -addMatchImageSnapshotCommand(); - -Cypress.Commands.add('getByDataCy', (selector) => { - cy.get(`[data-cy="${selector}"]`); -}); - -Cypress.Commands.add('register', (email = 'riot@qa.team', username = 'riot', password = '12345Qwert!') => { - cy.request('POST', '/users', { - email, - username, - password - }); -}); +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add('login', (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) + +import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command'; +import ArticlePageObject from '../support/pages/article.pageObject'; + +const articlePage = new ArticlePageObject(); + +addMatchImageSnapshotCommand(); + +Cypress.Commands.add('getByDataCy', (selector) => { + cy.get(`[data-cy="${selector}"]`); +}); + +Cypress.Commands.add('register', (email = 'riot@qa.team', username = 'riot', password = '12345Qwert!') => { + cy.request('POST', '/users', { + email, + username, + password + }); +}); + +Cypress.Commands.add('registerAndLogin', (email, username, password) => { + cy.register(email, username, password).then(() => { + cy.request('POST', '/users/login', { + user: { + email, + password, + }, + }).then((response) => { + cy.setCookie('drash_sess', response.body.user.token); + }); + }); +}); + +Cypress.Commands.add('createArticle', (title, description, body, tag) => { + articlePage.visit(); + cy.getByDataCy('article-title-field').type(title); + articlePage.typeDescription(description); + articlePage.typeBodyArticle(body); + articlePage.typeTag(tag); + articlePage.clickPublishBtn(); +}); diff --git a/cypress/support/pages/article.pageObject.js b/cypress/support/pages/article.pageObject.js new file mode 100644 index 00000000..e8d451a0 --- /dev/null +++ b/cypress/support/pages/article.pageObject.js @@ -0,0 +1,87 @@ +import PageObject from "../PageObject"; + +class ArticlePageObject extends PageObject { + url = "/#/editor"; + + get titleField() { + return cy.getByDataCy("article-title-field"); + } + + get descriptionField() { + return cy.getByDataCy("article-description-field"); + } + + get bodyArticleField() { + return cy.getByDataCy("article-body-textarea"); + } + + get tagField() { + return cy.getByDataCy("enter-tags-field"); + } + + get publishArticleBtn() { + return cy.getByDataCy("publish-article-btn"); + } + + get editArticleBtn() { + return cy.getByDataCy("edit-article-btn"); + } + + get deleteArticleBtn() { + return cy.getByDataCy("delete-article-btn"); + } + + get titleArticlePage() { + return cy.get("h1"); + } + + typeTitle(title) { + this.titleField.type(title); + } + + typeDescription(description) { + this.descriptionField.type(description); + } + + typeBodyArticle(body) { + this.bodyArticleField.type(body); + } + + typeTag(tag) { + this.tagField.first().type(tag); + } + + typeNewTitle(title) { + this.titleField.clear().type(title); + } + + typeNewDescription(description) { + this.descriptionField.clear().type(description); + } + + typeNewBodyArticle(body) { + this.bodyArticleField.clear().type(body); + } + + clickPublishBtn() { + this.publishArticleBtn.click(); + } + + clickEditArticleBtn() { + this.editArticleBtn.first().click(); + } + + clickDeleteArticleBtn() { + this.deleteArticleBtn.first().click(); + } + + assertArticlePage(title) { + cy.url().should("include", title); + } + + assertTitleArticlePage(title) { + this.titleArticlePage.should("contain", title); + } +} + +export default ArticlePageObject; diff --git a/cypress/support/pages/home.pageObject.js b/cypress/support/pages/home.pageObject.js index 9b31a941..24e3d44e 100644 --- a/cypress/support/pages/home.pageObject.js +++ b/cypress/support/pages/home.pageObject.js @@ -1,16 +1,15 @@ -import PageObject from '../PageObject'; - -class HomePageObject extends PageObject { - url = '/#/'; - - get usernameLink() { - return cy.getByDataCy('username-link'); - } - - assertHeaderContainUsername(username) { - this.usernameLink - .should('contain', username); - } -} - -export default HomePageObject; +import PageObject from "../PageObject"; + +class HomePageObject extends PageObject { + url = "/#/"; + + get usernameLink() { + return cy.getByDataCy("username-link"); + } + + assertHeaderContainUsername(username) { + this.usernameLink.should("contain", username); + } +} + +export default HomePageObject; diff --git a/cypress/support/pages/settings.PageObject.js b/cypress/support/pages/settings.PageObject.js new file mode 100644 index 00000000..63529e01 --- /dev/null +++ b/cypress/support/pages/settings.PageObject.js @@ -0,0 +1,63 @@ +import PageObject from '../PageObject' + +class SettingsPageObject extends PageObject { + url = '/#/settings' + + get usernameField() { + return cy.getByDataCy('update-username-field'); + } + + get updateSettingsBtn() { + return cy.getByDataCy('update-settings-btn'); + } + + get bioField() { + return cy.getByDataCy('update-bio-field'); + } + + get emailField() { + return cy.getByDataCy('update-email-field'); + } + + get passwordField() { + return cy.getByDataCy('update-password-field'); + } + + get logoutBtn() { + return cy.getByDataCy('or-click-here-to-logout-btn'); + } + + typeUsername(username) { + this.usernameField.clear().type(username); + } + + clickUpdateSettingsBtn() { + this.updateSettingsBtn.click(); + } + + assertProfilePage(username) { + return cy.url().should('contain', `/profile/${username}`); + } + + typeBio(bio) { + this.bioField.type(bio); + } + + typeEmail(email) { + this.emailField.clear().type(email); + } + + typePassword(password) { + this.passwordField.clear().type(password); + } + + clickLogoutBtn() { + this.logoutBtn.click(); + } + + assertLogout(expectedPath) { + cy.url().should('include', expectedPath); + } +} + +export default SettingsPageObject; \ No newline at end of file diff --git a/cypress/support/pages/signIn.pageObject.js b/cypress/support/pages/signIn.pageObject.js index 3a1e21c8..80dc9eed 100644 --- a/cypress/support/pages/signIn.pageObject.js +++ b/cypress/support/pages/signIn.pageObject.js @@ -1,34 +1,46 @@ -import PageObject from '../PageObject'; - -class SignInPageObject extends PageObject { - url = '/#/login'; - - get emailField() { - return cy.getByDataCy('email-sign-in'); - } - - get passwordField() { - return cy.getByDataCy('password-sign-in'); - } - - get signInBtn() { - return cy.getByDataCy('sign-in-btn'); - } - - typeEmail(email) { - this.emailField - .type(email); - } - - typePassword(password) { - this.passwordField - .type(password); - } - - clickSignInBtn() { - this.signInBtn - .click(); - } -} - -export default SignInPageObject; +import PageObject from '../PageObject'; + +class SignInPageObject extends PageObject { + url = '/#/login'; + + get emailField() { + return cy.getByDataCy('email-sign-in'); + } + + get passwordField() { + return cy.getByDataCy('password-sign-in'); + } + + get signInBtn() { + return cy.getByDataCy('sign-in-btn'); + } + + typeEmail(email) { + this.emailField + .type(email); + } + + typePassword(password) { + this.passwordField + .type(password); + } + + clickSignInBtn() { + this.signInBtn + .click(); + } + + get modalMessage() { + return cy.get('.swal-modal'); + } + + assertErrorMessage() { + this.modalMessage.should('contain', 'Login failed!'); + } + + assertLoginPage() { + cy.url().should('include', this.url); + } +} + +export default SignInPageObject; diff --git a/cypress/support/pages/signUp.PageObject.js b/cypress/support/pages/signUp.PageObject.js new file mode 100644 index 00000000..83aa5a33 --- /dev/null +++ b/cypress/support/pages/signUp.PageObject.js @@ -0,0 +1,55 @@ +import PageObject from '../PageObject'; + +class SignUpPageObject extends PageObject { + url = '/#/register'; + + get usernameField() { + return cy.getByDataCy('signup-username-field'); + } + + get emailField() { + return cy.getByDataCy('signup-email-field'); + } + + get passwordField() { + return cy.getByDataCy('signup-password-field'); + } + + get signUpBtn() { + return cy.getByDataCy('signup-button'); + } + + get modalMessage() { + return cy.get('.swal-modal'); + } + + typeUsername(username) { + this.usernameField.type(username); + } + + typeEmail(email) { + this.emailField.type(email); + } + + typePassword(password) { + this.passwordField.type(password); + } + + clickSignUpBtn() { + this.signUpBtn.click(); + } + + assertSuccessfulMessage() { + this.modalMessage.should('contain', 'Your registration was successful!'); + } + + assertErrorMessage() { + this.modalMessage.should('contain', 'Email already taken.'); + } + + assertSignUpPage() { + cy.url().should('include', this.url); + } +} + +export default SignUpPageObject; \ No newline at end of file diff --git a/cypress/support/pages/user.PageObject.js b/cypress/support/pages/user.PageObject.js new file mode 100644 index 00000000..b50a469a --- /dev/null +++ b/cypress/support/pages/user.PageObject.js @@ -0,0 +1,33 @@ +import PageObject from "../PageObject"; + +class UserPageObject extends PageObject { + visitUserPage(username) { + cy.visit(`/#/@${username}`); + } + + get followBtn() { + return cy.getByDataCy('follow-unfollow-btn'); + } + + get unfollowBtn() { + return cy.getByDataCy('follow-unfollow-btn'); + } + + assertCanFollowUser() { + this.followBtn.should('exist'); + } + + assertFollowingUser(username) { + this.unfollowBtn.should('exist'); + } + + clickFollowBtn() { + this.followBtn.click(); + } + + clickUnfollowBtn() { + this.unfollowBtn.click(); + } +} + +export default UserPageObject;