mirror of
https://github.com/Mueller-Patrick/Betterzon.git
synced 2024-12-23 20:15:13 +00:00
Compare commits
No commits in common. "1b57ebfb5344fbe49e611c26e7fbd6a9d9cc6924" and "b839994de0ef0d93085de671e9181647760a246e" have entirely different histories.
1b57ebfb53
...
b839994de0
|
@ -3,29 +3,22 @@ package stepdefs;
|
||||||
import io.cucumber.java.en.Given;
|
import io.cucumber.java.en.Given;
|
||||||
import io.cucumber.java.en.Then;
|
import io.cucumber.java.en.Then;
|
||||||
import io.cucumber.java.en.When;
|
import io.cucumber.java.en.When;
|
||||||
import org.openqa.selenium.By;
|
|
||||||
import org.openqa.selenium.WebElement;
|
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
|
||||||
|
|
||||||
public class FavoriteShopList {
|
public class FavoriteShopList {
|
||||||
@Given("^the user has at least (\\d+) favorite shop$")
|
@Given("^the user has at least (\\d+) favorite shop$")
|
||||||
public void the_user_has_at_least_favorite_shop(int arg1) throws Exception {
|
public void the_user_has_at_least_favorite_shop(int arg1) throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the profile page should open$")
|
@When("^the user clicks on favorite shops$")
|
||||||
public void the_profile_page_should_open() throws Exception {
|
public void the_user_clicks_on_favorite_shops() throws Exception {
|
||||||
WebElement profile_info_text = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("table.table.table-hover")));
|
|
||||||
assert(profile_info_text.isDisplayed());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^he should see his favorite shops list$")
|
@Then("^he should see his favorite shops list$")
|
||||||
public void he_should_see_his_favorite_shops_list() throws Exception {
|
public void he_Should_see_his_favorite_shops_list() throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^he clicks on delete a favorite shop entry$")
|
@When("^he clicks on delete a favorite shop entry$")
|
||||||
public void he_clicks_on_delete_a_favorite_shop_entry() throws Exception {
|
public void he_clicks_on_delete_a_favorite_shop_enty() throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the favorite shop entry should be deleted$")
|
@Then("^the favorite shop entry should be deleted$")
|
||||||
|
|
|
@ -4,5 +4,4 @@ import org.openqa.selenium.WebDriver;
|
||||||
|
|
||||||
public class Preconditions {
|
public class Preconditions {
|
||||||
public static WebDriver driver;
|
public static WebDriver driver;
|
||||||
public static final int delaySeconds = 7;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,6 @@ import io.cucumber.java.PendingException;
|
||||||
import io.cucumber.java.en.Given;
|
import io.cucumber.java.en.Given;
|
||||||
import io.cucumber.java.en.Then;
|
import io.cucumber.java.en.Then;
|
||||||
import io.cucumber.java.en.When;
|
import io.cucumber.java.en.When;
|
||||||
import org.openqa.selenium.By;
|
|
||||||
import org.openqa.selenium.WebElement;
|
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class PriceAlarm {
|
public class PriceAlarm {
|
||||||
@Given("^the user has at least (\\d+) price alarm set$")
|
@Given("^the user has at least (\\d+) price alarm set$")
|
||||||
|
@ -18,43 +12,42 @@ public class PriceAlarm {
|
||||||
|
|
||||||
@When("^the user clicks on the profile icon$")
|
@When("^the user clicks on the profile icon$")
|
||||||
public void the_user_clicks_on_the_profile_icon() throws Exception {
|
public void the_user_clicks_on_the_profile_icon() throws Exception {
|
||||||
WebElement profileButton = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
}
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[contains(text(),'profile')]")));
|
|
||||||
profileButton.click();
|
@Then("^the profile details popup should open$")
|
||||||
|
public void the_profile_details_popup_should_open() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@When("^the user clicks on price alarms$")
|
||||||
|
public void the_user_clicks_on_price_alarms() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Then("^the price alarm list should open$")
|
||||||
|
public void the_price_alarm_list_should_open() throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the price alarm list should contain at least (\\d+) entry$")
|
@Then("^the price alarm list should contain at least (\\d+) entry$")
|
||||||
public void the_price_alarm_list_should_contain_at_least_entry(int arg1) throws Exception {
|
public void the_price_alarm_list_should_contain_at_least_entry(int arg1) throws Exception {
|
||||||
WebElement alarmEntry = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("table.table.table-hover tr:nth-child(2)")));
|
|
||||||
|
|
||||||
assert (alarmEntry != null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Given("^the user is on the profile page$")
|
@Then("^the price alarm list should contain a maximum of (\\d+) entries per page$")
|
||||||
public void the_user_is_on_the_profile_page() throws Exception {
|
public void the_price_alarm_list_should_contain_a_maximum_of_entries_per_page(int arg1) throws Exception {
|
||||||
Preconditions.driver.get("https://www.betterzon.xyz/profile");
|
}
|
||||||
|
|
||||||
WebElement profile_info_text = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
@Given("^the user is on the price alarm list page$")
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("table.table.table-user-information")));
|
public void the_user_is_on_the_price_alarm_list_page() throws Exception {
|
||||||
assert (profile_info_text.isDisplayed());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user clicks on the \"([^\"]*)\" button next to a price alarm$")
|
@When("^the user clicks on the \"([^\"]*)\" button next to a price alarm$")
|
||||||
public void the_user_clicks_on_the_button_next_to_a_price_alarm(String arg1) throws Exception {
|
public void the_user_clicks_on_the_button_next_to_a_price_alarm(String arg1) throws Exception {
|
||||||
if (arg1.equals("remove")) {
|
|
||||||
WebElement entry = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("table.table.table-hover tr:nth-child(2)")));
|
|
||||||
|
|
||||||
if (entry == null) {
|
|
||||||
throw new Exception("Too few price alarm entries found!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebElement btn = entry.findElement(By.cssSelector("img.delete[src='../assets/images/Delete_icon-icons.com_55931.png']"));
|
@Then("^a popup should open asking the user to confirm the removal$")
|
||||||
|
public void a_popup_should_open_asking_the_user_to_confirm_the_removal() throws Exception {
|
||||||
btn.click();
|
|
||||||
} else if (arg1.equals("edit")) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@When("^the user confirms the removal of the price alarm$")
|
||||||
|
public void the_user_confirms_the_removal_of_the_price_alarm() throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the price alarm should be removed from the database$")
|
@Then("^the price alarm should be removed from the database$")
|
||||||
|
|
|
@ -4,7 +4,9 @@ import io.cucumber.java.PendingException;
|
||||||
import io.cucumber.java.en.Given;
|
import io.cucumber.java.en.Given;
|
||||||
import io.cucumber.java.en.Then;
|
import io.cucumber.java.en.Then;
|
||||||
import io.cucumber.java.en.When;
|
import io.cucumber.java.en.When;
|
||||||
import org.openqa.selenium.*;
|
import org.openqa.selenium.By;
|
||||||
|
import org.openqa.selenium.Keys;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||||
|
|
||||||
|
@ -13,8 +15,8 @@ public class SearchProduct {
|
||||||
public void the_user_is_on_the_landing_page() throws Exception {
|
public void the_user_is_on_the_landing_page() throws Exception {
|
||||||
//throw new PendingException();
|
//throw new PendingException();
|
||||||
Preconditions.driver.get("https://betterzon.xyz");
|
Preconditions.driver.get("https://betterzon.xyz");
|
||||||
WebElement logo = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
WebElement logo = (new WebDriverWait(Preconditions.driver, 10))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("a.navbar-brand")));
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".navbar-brand")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user enters the search term \"([^\"]*)\" and clicks search$")
|
@When("^the user enters the search term \"([^\"]*)\" and clicks search$")
|
||||||
|
@ -22,99 +24,49 @@ public class SearchProduct {
|
||||||
WebElement searchField = Preconditions.driver.findElement(By.cssSelector(".ng-untouched.ng-pristine.ng-valid"));
|
WebElement searchField = Preconditions.driver.findElement(By.cssSelector(".ng-untouched.ng-pristine.ng-valid"));
|
||||||
searchField.sendKeys(searchTerm);
|
searchField.sendKeys(searchTerm);
|
||||||
searchField.sendKeys(Keys.ENTER);
|
searchField.sendKeys(Keys.ENTER);
|
||||||
WebElement logo = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
WebElement logo = (new WebDriverWait(Preconditions.driver, 10))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".navbar-brand")));
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".navbar-brand")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the user should see the error page \"([^\"]*)\"$")
|
@Then("^the user should see the error page \"([^\"]*)\"$")
|
||||||
public void the_user_should_see_the_error_page(String arg0) throws Exception {
|
public void the_user_should_see_the_error_page(String arg0) throws Exception {
|
||||||
WebElement noProdsFoundMsg = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
WebElement noProdsFoundMsg = (new WebDriverWait(Preconditions.driver, 10))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[contains(text(),'No Products found!')]")));
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".ng-star-inserted")));
|
||||||
assert (noProdsFoundMsg.isDisplayed());
|
assert(noProdsFoundMsg.getText().contains("No Products found!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Given("^the user is not logged in$")
|
@Given("^the user is not logged in$")
|
||||||
public void the_user_is_not_logged_in() throws Exception {
|
public void the_user_is_not_logged_in() throws Exception {
|
||||||
try {
|
|
||||||
WebElement logoutButton = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[contains(text(),'log out')]")));
|
|
||||||
|
|
||||||
logoutButton.click();
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Given("^the user is logged in$")
|
@Given("^the user is logged in$")
|
||||||
public void the_user_is_logged_in() throws Exception {
|
public void the_user_is_logged_in() throws Exception {
|
||||||
try {
|
|
||||||
WebElement loginButton = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[contains(text(),'sign in')]")));
|
|
||||||
loginButton.click();
|
|
||||||
|
|
||||||
WebElement usernameField = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.id("username")));
|
|
||||||
usernameField.sendKeys("Selenium");
|
|
||||||
|
|
||||||
WebElement passwordField = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.id("password")));
|
|
||||||
passwordField.sendKeys("Selenium");
|
|
||||||
|
|
||||||
WebElement loginBtn = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.className("btn_signin")));
|
|
||||||
loginBtn.click();
|
|
||||||
|
|
||||||
} catch (TimeoutException e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the user should see a list of products$")
|
@Then("^the user should see a list of products$")
|
||||||
public void the_user_should_see_a_list_of_products() throws Exception {
|
public void the_user_should_see_a_list_of_products() throws Exception {
|
||||||
WebElement product = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
WebElement product = (new WebDriverWait(Preconditions.driver, 10))
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".row.p-2.bg-white.border.rounded")));
|
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".productItem.ng-star-inserted")));
|
||||||
assert (product.isDisplayed());
|
assert(product.isDisplayed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user clicks on the first product$")
|
@When("^the user clicks on the first product$")
|
||||||
public void the_user_clicks_on_the_first_product() throws Exception {
|
public void the_user_clicks_on_the_first_product() throws Exception {
|
||||||
WebElement productDetailsBtn = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector(".row.p-2.bg-white.border.rounded button.btn.btn-primary.btn-sm")));
|
|
||||||
productDetailsBtn.click();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the user should see the product detail page$")
|
@Then("^the user should see the product detail page$")
|
||||||
public void the_user_should_see_the_product_detail_page() throws Exception {
|
public void the_user_should_see_the_product_detail_page() throws Exception {
|
||||||
WebElement productTitle = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("div.productTitle")));
|
|
||||||
assert (productTitle.isDisplayed());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the set price alarm box should show \"([^\"]*)\"$")
|
@Then("^the set price alarm box should show \"([^\"]*)\"$")
|
||||||
public void the_set_price_alarm_box_should_show(String arg0) throws Exception {
|
public void the_set_price_alarm_box_should_show(String arg0) throws Exception {
|
||||||
WebElement alarmBox = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("div.priceAlarm")));
|
|
||||||
if (arg0.equals("Login to set a price alarm")) {
|
|
||||||
assert (alarmBox.getText().equals("Login to set a price alarm"));
|
|
||||||
} else {
|
|
||||||
assert (alarmBox.isDisplayed());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@When("^the user sets a price alarm$")
|
@When("^the user sets a price alarm$")
|
||||||
public void the_user_sets_a_price_alarm() throws Exception {
|
public void the_user_sets_a_price_alarm() throws Exception {
|
||||||
WebElement alarmBoxField = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("div.priceAlarm input")));
|
|
||||||
alarmBoxField.sendKeys("12345");
|
|
||||||
WebElement alarmBox = (new WebDriverWait(Preconditions.driver, Preconditions.delaySeconds))
|
|
||||||
.until(ExpectedConditions.elementToBeClickable(By.cssSelector("div.priceAlarm")));
|
|
||||||
alarmBox.click();
|
|
||||||
|
|
||||||
assert (alarmBox.isDisplayed() && alarmBoxField.isDisplayed());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Then("^the user should receive an email confirming the price alarm$")
|
@Then("^the user should receive an email confirming the price alarm$")
|
||||||
public void the_user_should_receive_an_email_confirming_the_price_alarm() throws Exception {
|
public void the_user_should_receive_an_email_confirming_the_price_alarm() throws Exception {
|
||||||
assert (true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ Feature: Favorite Shop List
|
||||||
And the user is logged in
|
And the user is logged in
|
||||||
And the user has at least 1 favorite shop
|
And the user has at least 1 favorite shop
|
||||||
When the user clicks on the profile icon
|
When the user clicks on the profile icon
|
||||||
Then the profile page should open
|
Then the profile details popup should open
|
||||||
|
When the user clicks on favorite shops
|
||||||
Then he should see his favorite shops list
|
Then he should see his favorite shops list
|
||||||
|
|
||||||
Scenario: Remove Favorite Shop Entry
|
Scenario: Remove Favorite Shop Entry
|
||||||
|
@ -13,6 +14,7 @@ Feature: Favorite Shop List
|
||||||
And the user is logged in
|
And the user is logged in
|
||||||
And the user has at least 1 favorite shop
|
And the user has at least 1 favorite shop
|
||||||
When the user clicks on the profile icon
|
When the user clicks on the profile icon
|
||||||
Then the profile page should open
|
Then the profile details popup should open
|
||||||
When he clicks on delete a favorite shop entry
|
When the user clicks on favorite shops
|
||||||
|
And he clicks on delete a favorite shop entry
|
||||||
Then the favorite shop entry should be deleted
|
Then the favorite shop entry should be deleted
|
||||||
|
|
|
@ -5,22 +5,23 @@ Feature: Price Alarms
|
||||||
And the user is logged in
|
And the user is logged in
|
||||||
And the user has at least 1 price alarm set
|
And the user has at least 1 price alarm set
|
||||||
When the user clicks on the profile icon
|
When the user clicks on the profile icon
|
||||||
Then the profile page should open
|
Then the profile details popup should open
|
||||||
|
When the user clicks on price alarms
|
||||||
|
Then the price alarm list should open
|
||||||
And the price alarm list should contain at least 1 entry
|
And the price alarm list should contain at least 1 entry
|
||||||
|
And the price alarm list should contain a maximum of 20 entries per page
|
||||||
|
|
||||||
Scenario: Remove a price alarm
|
Scenario: Remove a price alarm
|
||||||
Given the user is on the landing page
|
Given the user is on the price alarm list page
|
||||||
And the user is logged in
|
And the user is logged in
|
||||||
When the user clicks on the profile icon
|
|
||||||
Then the profile page should open
|
|
||||||
When the user clicks on the "remove" button next to a price alarm
|
When the user clicks on the "remove" button next to a price alarm
|
||||||
|
Then a popup should open asking the user to confirm the removal
|
||||||
|
When the user confirms the removal of the price alarm
|
||||||
Then the price alarm should be removed from the database
|
Then the price alarm should be removed from the database
|
||||||
|
|
||||||
Scenario: Edit a price alarm
|
Scenario: Edit a price alarm
|
||||||
Given the user is on the landing page
|
Given the user is on the price alarm list page
|
||||||
And the user is logged in
|
And the user is logged in
|
||||||
When the user clicks on the profile icon
|
|
||||||
Then the profile page should open
|
|
||||||
When the user clicks on the "edit" button next to a price alarm
|
When the user clicks on the "edit" button next to a price alarm
|
||||||
Then a popup should open where the user can edit the alarm
|
Then a popup should open where the user can edit the alarm
|
||||||
When the user clicks on the "save changes" button
|
When the user clicks on the "save changes" button
|
||||||
|
|
|
@ -12,7 +12,7 @@ Feature: Search a Product
|
||||||
Then the user should see a list of products
|
Then the user should see a list of products
|
||||||
When the user clicks on the first product
|
When the user clicks on the first product
|
||||||
Then the user should see the product detail page
|
Then the user should see the product detail page
|
||||||
And the set price alarm box should show "Login to set a price alarm"
|
And the set price alarm box should show "Log in to continue"
|
||||||
|
|
||||||
Scenario: User is logged in, searches for known product
|
Scenario: User is logged in, searches for known product
|
||||||
Given the user is on the landing page
|
Given the user is on the landing page
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Betterzon
|
# Betterzon
|
||||||
Website: https://betterzon.p4ddy.com<br>
|
Website: https://www.betterzon.xyz<br>
|
||||||
Blog: https://blog.betterzon.p4ddy.com<br>
|
Blog: https://blog.betterzon.xyz<br>
|
||||||
Wiki: https://github.com/Mueller-Patrick/Betterzon/wiki
|
Wiki: https://github.com/Mueller-Patrick/Betterzon/wiki
|
||||||
|
|
||||||
# Code Quality
|
# Code Quality
|
||||||
|
@ -8,5 +8,5 @@ Wiki: https://github.com/Mueller-Patrick/Betterzon/wiki
|
||||||
[![Code Coverage](https://img.shields.io/badge/coverage-81%25-green)](https://ci.betterzon.xyz)
|
[![Code Coverage](https://img.shields.io/badge/coverage-81%25-green)](https://ci.betterzon.xyz)
|
||||||
|
|
||||||
# Project Status
|
# Project Status
|
||||||
[![Website Status](https://img.shields.io/website?label=betterzon.p4ddy.com&style=for-the-badge&url=https%3A%2F%2Fbetterzon.p4ddy.com)](https://betterzon.p4ddy.com)
|
[![Website Status](https://img.shields.io/website?label=www.betterzon.xyz&style=for-the-badge&url=https%3A%2F%2Fwww.betterzon.xyz)](https://www.betterzon.xyz)
|
||||||
[![Blog Status](https://img.shields.io/website?label=blog.betterzon.p4ddy.com&style=for-the-badge&url=https%3A%2F%2Fblog.betterzon.p4ddy.com)](https://blog.betterzon.p4ddy.com)
|
[![Blog Status](https://img.shields.io/website?label=blog.betterzon.xyz&style=for-the-badge&url=https%3A%2F%2Fblog.betterzon.xyz)](https://blog.betterzon.xyz)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user