diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..87574bf8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# For lazy people who set up Windows git with windows style checkout/commit +# as it breaks scripts that run after setting up container +*.sh text eol=lf diff --git a/.gitignore b/.gitignore index 29d65f8f..517f274d 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,9 @@ target/ # upload folder ecomap/www/uploads/ + +# Vagrant files +.vagrant/ + +# idea +.idea/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..8a6a36a4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,53 @@ +FROM ubuntu + +RUN echo debconf debconf/frontend select Noninteractive | debconf-set-selections +RUN apt-get update && apt-get install -y \ + apache2 \ + git \ + libapache2-mod-wsgi \ + libffi-dev \ + libmysqlclient-dev \ + libssl-dev \ + libxml2-dev \ + libxslt1-dev \ + memcached \ + mysql-client \ + python-dev \ + python-pip \ +&& apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN echo "[client]" >> /etc/mysql/conf.d/mysql.cnf +RUN echo "protocol=tcp" >> /etc/mysql/conf.d/mysql.cnf +RUN echo "default-character-set = utf8" >> /etc/mysql/conf.d/mysql.cnf + +COPY ecomap /opt/ecomap +COPY requirements.txt /tmp/ + +RUN pip install --upgrade pip +RUN pip install -r /tmp/requirements.txt + +ENV PRODROOT=/opt/ecomap +ENV PYSRCROOT=${PRODROOT}/src/python +ENV CONFROOT=${PRODROOT}/etc +ENV PYTHONPATH=${PRODROOT}/src/python +ENV PYTHON=/etc/python +ENV PYTHON_EGG_CACHE=/tmp/.python-eggs +ENV STATICROOT=${PRODROOT}/www/ + +RUN a2enmod wsgi +COPY ecomap/etc/_ecomap_apache.conf /etc/apache2/sites-available/ecomap.conf +RUN a2ensite ecomap +RUN a2dissite 000-default + +RUN echo "ServerName localhost" >> /etc/apache2/conf-available/fqdn.conf +RUN a2enconf fqdn + +ENV APACHE_RUN_USER www-data +ENV APACHE_RUN_GROUP www-data +ENV APACHE_LOG_DIR /var/log/apache2 +ENV APACHE_PID_FILE /var/run/apache2.pid +ENV APACHE_LOCK_DIR /var/run + +CMD ["/opt/ecomap/bin/db_deploy_and_run.sh"] + +EXPOSE 80 diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 00000000..d44622d3 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,33 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox' + +Vagrant.configure("2") do |config| + + config.vm.box = "ubuntu/xenial64" + config.vm.network "forwarded_port", guest: 80, host: 8080 + config.vm.network "forwarded_port", guest: 3306, host: 8081 + + config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # # if needed + # vb.gui = true + vb.cpus = 2 + vb.memory = "2048" + end + # Comment next line to save ~ 200 Mb of disk space :) + config.vm.provision "shell", path: "guestadditions.sh" + + config.vm.provision "docker" do |d| + d.pull_images "ubuntu" + d.pull_images "mariadb" + d.build_image "/vagrant", + args: "-t web" + d.run "mariadb", + cmd: "mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --init_connect='SET collation_connection = utf8_unicode_ci'", + args: "-p 3306:3306 -e MYSQL_ROOT_PASSWORD=megasecret -e MYSQL_DATABASE=ecomap" + d.run "web", + args: "-p 80:80 --link mariadb:mysql" + end +end diff --git a/compose.sh b/compose.sh new file mode 100755 index 00000000..40fa5aed --- /dev/null +++ b/compose.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +sudo docker-compose down +sudo docker-compose build +sudo docker-compose up diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..1c8d390b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: '2' +services: + web: + build: . + ports: + - "80:80" + depends_on: + - mysql + links: + - mysql:mysql + mysql: + image: mariadb + ports: + - "3306:3306" + environment: + - MYSQL_ROOT_PASSWORD=megasecret + - MYSQL_DATABASE=ecomap + command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --init_connect='SET collation_connection = utf8_unicode_ci' diff --git a/ecomap/bin/db_deploy_and_run.sh b/ecomap/bin/db_deploy_and_run.sh new file mode 100755 index 00000000..8833c10a --- /dev/null +++ b/ecomap/bin/db_deploy_and_run.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" +PRODROOT=${SCRIPT_DIR%/*} +echo "Product directory: $PRODROOT" +DBSCRIPTROOT=${PRODROOT}/db/ecomap +cd $DBSCRIPTROOT + +while ! mysqladmin ping -h mysql --silent; do + echo "Waiting 5 seconds for mysql" + sleep 5 +done + +mysql -h mysql -u root -pmegasecret < DEPLOY.sql + +/usr/sbin/apache2ctl -D FOREGROUND diff --git a/ecomap/bin/ecomap_config_builder.sh b/ecomap/bin/ecomap_config_builder.sh index 9b10123a..b06c6270 100755 --- a/ecomap/bin/ecomap_config_builder.sh +++ b/ecomap/bin/ecomap_config_builder.sh @@ -1,7 +1,9 @@ -# !/usr/bin/env bash -# Script run config builder +#!/usr/bin/env bash +# Script run config builder -PRODROOT=~/home/workspace/test_ecomap/ecomap +SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )" +PRODROOT=${SCRIPT_DIR%/*} +echo "Product directory: $PRODROOT" PYSRCROOT=${PRODROOT}/src/python CONFROOT=${PRODROOT}/etc PYTHONPATH=$PYSRCROOT diff --git a/ecomap/db/ecomap/DEPLOY.sql b/ecomap/db/ecomap/DEPLOY.sql new file mode 100644 index 00000000..347d266d --- /dev/null +++ b/ecomap/db/ecomap/DEPLOY.sql @@ -0,0 +1,3 @@ +USE ecomap; +SOURCE CREATE_DB.sql; +SOURCE INSERT_DATA.sql; diff --git a/ecomap/etc/_ecomap_apache.conf b/ecomap/etc/_ecomap_apache.conf index 7ab8765e..68259a2d 100644 --- a/ecomap/etc/_ecomap_apache.conf +++ b/ecomap/etc/_ecomap_apache.conf @@ -1,18 +1,18 @@ - + - # --- Configure VirtualHost --- + # --- Configure VirtualHost --- ServerName www.ecomap.new ServerAdmin admin.email@gmail.com ServerAlias ecomap.new - DocumentRoot "/path/to/project/directory/Lv-164.UI/" + DocumentRoot "/opt/ecomap/www" # --- Configure WSGI Listening App(s) --- - WSGIDaemonProcess ecomap user=admin_name group=admin_name threads=5 processes=5 - WSGIScriptAlias / /path/to/project/directory/Lv-164.UI/ecomap/www/ecomap.wsgi + WSGIDaemonProcess ecomap user=www-data group=www-data threads=5 processes=5 + WSGIScriptAlias / /opt/ecomap/www/ecomap.wsgi - + WSGIProcessGroup ecomap WSGIApplicationGroup %{GLOBAL} WSGIScriptReloading Off @@ -21,16 +21,16 @@ # --- Configure Static Files --- - Alias /uploads/ /path/to/project/directory/Lv-164.UI/ecomap/www/uploads/ - Alias /js/ /path/to/project/directory/Lv-164.UI/ecomap/www/media/js/ - Alias /css/ /path/to/project/directory/Lv-164.UI/ecomap/www/media/css/ - Alias /image/ /path/to/project/directory/Lv-164.UI/ecomap/www/media/image/ - Alias /templates/ /path/to/project/directory/Lv-164.UI/ecomap/www/templates/ - Alias /fonts/ /path/to/project/directory/Lv-164.UI/ecomap/www/media/fonts/ + Alias /uploads/ /opt/ecomap/www/uploads/ + Alias /js/ /opt/ecomap/www/media/js/ + Alias /css/ /opt/ecomap/www/media/css/ + Alias /image/ /opt/ecomap/www/media/image/ + Alias /templates/ /opt/ecomap/www/templates/ + Alias /fonts/ /opt/ecomap/www/media/fonts/ # --- Configure Apache logging --- - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined + ErrorLog ${APACHE_LOG_DIR}/ecomap_error.log + CustomLog ${APACHE_LOG_DIR}/ecomap_access.log combined LogLevel debug diff --git a/ecomap/etc/_log.conf b/ecomap/etc/_log.conf index bea3a0b6..d5ec90f0 100644 --- a/ecomap/etc/_log.conf +++ b/ecomap/etc/_log.conf @@ -2,21 +2,21 @@ keys=root [handlers] -keys=syslogHandler +keys=consoleHandler [formatters] -keys=syslogFormat +keys=simpleFormatter [logger_root] level=DEBUG -handlers=syslogHandler +handlers=consoleHandler -[handler_syslogHandler] -class=handlers.SysLogHandler -formatter=syslogFormat -args=('/dev/log', handlers.SysLogHandler.LOG_LOCAL6) +[handler_consoleHandler] +class=StreamHandler +formatter=simpleFormatter +args=(sys.stdout,) -[formatter_syslogFormat] +[formatter_simpleFormatter] format=ecomap: %(asctime)s %(levelname)s [%(module)s:%(lineno)d][%(funcName)s]%(threadName)s - %(message)s datefmt=%d/%m/%y %H:%M:%S diff --git a/ecomap/etc/db.conf b/ecomap/etc/db.conf index 611dfa0f..8b9ec4b0 100644 --- a/ecomap/etc/db.conf +++ b/ecomap/etc/db.conf @@ -1,14 +1,14 @@ [db] -db=ecomap_db -rw.host=localhost +db=ecomap +rw.host=mysql rw.port=eval(3306) rw.user=root -rw.password=root +rw.password=megasecret rw.pool_size = eval(3) -ro.host=localhost +ro.host=mysql ro.port=eval(3306) ro.user=root -ro.password=root +ro.password=megasecret ro.pool_size = eval(3) connection_lifetime=eval(5) connection_retries=eval(3) diff --git a/guestadditions.sh b/guestadditions.sh new file mode 100644 index 00000000..b17c8099 --- /dev/null +++ b/guestadditions.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +VBGAVERSION=5.1.18 + +sudo apt-get update -y + +sudo apt-get dist-upgrade -y + +sudo apt-get install linux-headers-$(uname -r) build-essential dkms -y + +sudo wget -c http://download.virtualbox.org/virtualbox/$VBGAVERSION/VBoxGuestAdditions_$VBGAVERSION.iso \ +-O /opt/VBoxGuestAdditions_$VBGAVERSION.iso > /dev/null 2>&1 + +sudo mount -o loop,ro /opt/VBoxGuestAdditions_$VBGAVERSION.iso /mnt + +echo "yes" | sudo sh /mnt/VBoxLinuxAdditions.run uninstall + +echo "yes" | sudo sh /mnt/VBoxLinuxAdditions.run --nox11 + +sudo groupadd vboxusers + +sudo usermod -a -G vboxusers $USER + +sudo umount /mnt + +sudo rm /opt/*.iso diff --git a/page_object_ecomap/__init__.py b/page_object_ecomap/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/page_object_ecomap/framework/.gitignore b/page_object_ecomap/framework/.gitignore new file mode 100644 index 00000000..78faa368 --- /dev/null +++ b/page_object_ecomap/framework/.gitignore @@ -0,0 +1,3 @@ +chromedriver +.idea/ + diff --git a/page_object_ecomap/framework/BasePage.py b/page_object_ecomap/framework/BasePage.py new file mode 100644 index 00000000..f87a9c1a --- /dev/null +++ b/page_object_ecomap/framework/BasePage.py @@ -0,0 +1,45 @@ +from selenium.common.exceptions import NoSuchElementException +from selenium.webdriver import ActionChains +from page_object_ecomap.framework.Locators import HomePageLocator, LogoLocator + + +class BasePage: + + def __init__(self, driver, base_url=HomePageLocator.BASE_URL): + self.driver = driver + self.base_url = base_url + + def open(self, url=""): + url = self.base_url + url + self.driver.get(url) + + def is_logo_present(self): + return self.is_element_present(*LogoLocator.LOGO) + + def find_element(self, *locator): + return self.driver.find_element(*locator) + + def click(self, *locator): + self.driver.find_element(*locator).click() + + def type(self, text, *locator): + element = self.driver.find_element(*locator) + element.send_keys(text) + + def get_title(self): + return self.driver.title + + def get_current_url(self): + return self.driver.current_url + + def hover(self, *locator): + element = self.driver.find_element(*locator) + hover = ActionChains(self.driver).move_to_element(element) + hover.perform() + + def is_element_present(self, *locator): + try: + self.find_element(*locator) + except NoSuchElementException: + return False + return True diff --git a/page_object_ecomap/framework/Locators.py b/page_object_ecomap/framework/Locators.py new file mode 100644 index 00000000..2a71f8f9 --- /dev/null +++ b/page_object_ecomap/framework/Locators.py @@ -0,0 +1,85 @@ +from selenium.webdriver.common.by import By + + +class LogoLocator: + LOGO = (By.XPATH, "//img[contains(@src, 'logo.png')]") + + +class NavigationLocator: + NAV_BTN = (By.XPATH, "//button[@data-target='#navMenu']") + RESOURCE = (By.XPATH, "//a[@ng-show='faqTitles']") + STATISTIC = (By.XPATH, "//a[contains(@href, 'statistic')]") + ADD_PROBLEM = (By.XPATH, '//*[@href="/#/addProblem"]') + + +class MapLocator: + MAP = (By.ID, "map") + + +class Filter: + FILTER_BTN = (By.XPATH, "//div[@class='filter-toogle']") + # filter form + + +class HomePageLocator(LogoLocator, NavigationLocator, MapLocator): + BASE_URL = "http://localhost" + URL = BASE_URL + "/#/map" + LOG_IN = (By.XPATH, "//a[contains(@href,'login')]") + REGISTER = (By.XPATH, "//a[contains(@href,'register')]") + USER_PROFILE = (By.XPATH, "//a[@href='/#/user_profile/info']") + STATISTIC_PAGE_UNREGISTERED = "ul.nav:nth-child(1) > li:nth-child(2) > a:nth-child(1)" + + +class LoginPageLocator: + EMAIL = (By.XPATH, "//input[@type='email']") + PASSWORD = (By.XPATH, "//input[@type='password']") + SUBMIT = (By.XPATH, "//button[@type='submit']") + URL = "/#/login" + +class RegisterPageLocator: + REG_URL = "/#/register" + REG_BLOCK = (By.XPATH, '//*[@id="registerForm"]') + EMAIL = (By.XPATH, '//*[@id="email"]') + NAME = (By.XPATH, '//*[@id="name"]') + SURNAME = (By.XPATH, '//*[@id="surname"]') + NICKNAME = (By.XPATH, '//*[@id="nickname"]') + PASSWORD = (By.XPATH, '//*[@id="password"]') + CONFIRMPASSWORD = (By.XPATH, '//*[@id="pass_confirm"]') + SUBMIT_BUTTON = (By.XPATH, '//*[@id="registerForm"]/div[1]/div[2]/button') + +class HomeUserPageLocator(LogoLocator, NavigationLocator, MapLocator): + URL = "/#/map" + USER_PROFILE_LINK = (By.ID, "navMenu") + LOGOUT_LINK = (By.XPATH, "//a[@ng-click='Logout()']") + USER_CREDENTIALS = (By.XPATH, '//*[@id="navMenu"]/ul[2]/li[1]/a') + +class AddProblemPageLocator: + URL = '/#/addProblem' + +class Location_Locator(object): + FIND_ME = (By.XPATH, "//*[@class = 'form-group col-lg-6']") + LATITUDE = (By.XPATH, '//*[@id="latitude"]') + LONGITUDE = (By.XPATH, '//*[@id="longitude"]') + LOCATION_WIDGET = (By.XPATH, '//*[@class ="gmnoprint"]') + + +class UserProfileLocator(object): + URL = "/#/user_profile/info" + OLD_PASS = (By.XPATH, "//input[@id='old_pass']") + NEW_PASS = (By.XPATH, "//input[@id='new_pass']") + NEW_PASS_CONFIRM = (By.XPATH, "//input[@id='new_pass_confirm']") + SUBMIT = (By.XPATH, "//button[@type='submit']") + SUCCESS_POPUP = (By.XPATH, '//*[@id="toast-container"]/div') + +class StatisticPageLocator(object): + URL = "/#/statistic" + COMMENTS_XPATH = "//ul[3][contains(@class,'all-statistic')]/li[1][text() != '']" + COMMENTS_CSS = "ul.all-statistic:nth-child(3) > li:nth-child(1)" + COMMENTS_LIST_XPATH = "//ul[@ng-repeat = 'problemcomm in problCommStats']" + SUBSCRIPTIONS_XPATH = "//ul[2][contains(@class,'all-statistic')]/li[1][text() != '']" + SUBSCRIPTIONS_CSS = "ul.all-statistic:nth-child(2) > li:nth-child(1)" + SUBSCRIPTIONS_LIST_XPATH = "//ul[@ng-repeat = 'subscription in subscriptions']" + PROBLEMS_XPATH = "//ul[1][contains(@class,'all-statistic')]/li[1][text() != '']" + PROBLEMS_CSS = "ul.all-statistic:nth-child(1) > li:nth-child(1)" + IN_SEVERITIES_LIST_XPATH = "//ul[@ng-repeat = 'severity in severities']" + diff --git a/page_object_ecomap/framework/Pages.py b/page_object_ecomap/framework/Pages.py new file mode 100644 index 00000000..20193717 --- /dev/null +++ b/page_object_ecomap/framework/Pages.py @@ -0,0 +1,234 @@ +from selenium.common.exceptions import TimeoutException +from page_object_ecomap.framework.BasePage import BasePage +from page_object_ecomap.framework.Locators import * +from math import fabs +from selenium.webdriver.support.wait import WebDriverWait +import requests +import json +from selenium.webdriver.support import expected_conditions as EC + + +class HomePage(BasePage): + def get_login_page(self): + self.click(*HomePageLocator.LOG_IN) + return LoginPage(self.driver) + + def get_user_profile_page(self): + self.click(*HomePageLocator.USER_PROFILE) + return UserProfilePage(self.driver) + + def get_expected_url(self): + return self.base_url + + def get_registration_page(self): + self.click(*HomePageLocator.REGISTER) + return Registration(self.driver) + + +class HomeUserPage(BasePage): + def get_expected_url(self): + return self.base_url + HomeUserPageLocator.URL + + def is_logout_btn_present(self): + return self.is_element_present(*HomeUserPageLocator.LOGOUT_LINK) + + def user_credentials_btn_is_present(self): + return self.find_element(*HomeUserPageLocator.USER_CREDENTIALS).text + + def is_user_profile_link_present(self): + return self.is_element_present(*HomeUserPageLocator.USER_PROFILE_LINK) + + def click_on_add_problem(self): + self.driver.find_element(*NavigationLocator.ADD_PROBLEM).click() + return AddProblemPage(self.driver) + + def is_add_problem_tab_present(self): + return self.is_element_present(*NavigationLocator.ADD_PROBLEM) + + +class LoginPage(BasePage): + def login(self, login_name, password): + self.type(login_name, *LoginPageLocator.EMAIL) + self.type(password, *LoginPageLocator.PASSWORD) + self.click(*LoginPageLocator.SUBMIT) + return HomeUserPage(self.driver) + + def get_expected_url(self): + return self.base_url + LoginPageLocator.URL + + +class AddProblemPage(BasePage): + + def check_presence_of_coordinates(self, driver): + try: + latitude = driver.find_element(*Location_Locator.LATITUDE).get_attribute("value") + longitude = driver.find_element(*Location_Locator.LONGITUDE).get_attribute("value") + return float(latitude), float(longitude) + except (TypeError, ValueError): + return False + + def click_on_find_me(self): + try: + self.driver.find_element(*Location_Locator.FIND_ME).click() + latitude, longitude = WebDriverWait(self.driver, 5).until(self.check_presence_of_coordinates) + found_coordinates = [latitude, longitude] + return found_coordinates + except TimeoutException: + return None + + def check_location(self, found_coordinates, actual_coordinates): + try: + if fabs(actual_coordinates[0] - found_coordinates[0]) < 0.1 \ + and fabs(actual_coordinates[1] - found_coordinates[1]) < 0.1: + return True + else: + return False + except (IndexError, TypeError): + return False + + def get_actual_coordinates(self): + try: + send_url = 'http://freegeoip.net/json/' + r = requests.get(send_url) + j = json.loads(r.text) + lat = j['latitude'] + lon = j['longitude'] + # ip = j['ip'] + actual_coordinates = [lat, lon] + except Exception: + actual_coordinates = None + return actual_coordinates + + def is_location_widget_present(self): + return self.is_element_present(*Location_Locator.LOCATION_WIDGET) + + def get_reason_of_fail(self): + actual_coordinates = self.get_actual_coordinates() + found_coordinates = self.click_on_find_me() + if found_coordinates is None and actual_coordinates is None: + message = "Can't find coordinates by application and outside service" + elif actual_coordinates is None: + message = "Can't find coordinates by outside service" + elif found_coordinates is None: + message = "Can't find coordinates by application" + else: + message = "Actual coordinates don't equal found coordinates" + return message + + def get_expected_url(self): + return self.base_url + AddProblemPageLocator.URL + + +class Registration(BasePage): + def reg(self, email, name, surname, nickname, password, confirmpassword): + self.type(email, *RegisterPageLocator.EMAIL) + self.type(name, *RegisterPageLocator.NAME) + self.type(surname, *RegisterPageLocator.SURNAME) + self.type(nickname, *RegisterPageLocator.NICKNAME) + self.type(password, *RegisterPageLocator.PASSWORD) + self.type(confirmpassword, *RegisterPageLocator.CONFIRMPASSWORD) + self.click(*RegisterPageLocator.SUBMIT_BUTTON) + + def get_expected_reg_url(self): + return self.base_url + RegisterPageLocator.REG_URL + + def wait_linked_text_changed(self): + _driver = self.driver + WebDriverWait(self.driver, 5).until(lambda _driver: _driver.find_element(*HomeUserPageLocator.USER_CREDENTIALS).text != 'УВІЙТИ') + + +class UserProfilePage(BasePage): + def change_pwd(self, old_password, new_password, confirm_password): + self.type(old_password, *UserProfileLocator.OLD_PASS) + self.type(new_password, *UserProfileLocator.NEW_PASS) + self.type(confirm_password, *UserProfileLocator.NEW_PASS_CONFIRM) + self.click(*UserProfileLocator.SUBMIT) + return HomeUserPage(self.driver) + + def is_success_popup_present(self): + _d = self.driver + try: + WebDriverWait(_d, 5).until(lambda _d: _d.find_element(*UserProfileLocator.SUCCESS_POPUP)) + except Exception: + return False + return True + + def get_expected_url(self): + return self.base_url + UserProfileLocator.URL + + +class StatisticPage(BasePage): + + drv = None + _problems = None + _subscriptions = None + _comments = None + _photos = None + + def set_driver(self, webdriver): + self.drv = webdriver + + def get_expected_url(self): + return self.base_url + StatisticPageLocator.URL + + def get_current_url(self): + return self.driver.current_url + + def goToStatisticPage(self): + _d = self.drv + wait = WebDriverWait(_d , 40) + wait.until(lambda _d: _d.find_element_by_css_selector(HomePageLocator.STATISTIC_PAGE_UNREGISTERED)) + element = _d.find_element_by_css_selector(HomePageLocator.STATISTIC_PAGE_UNREGISTERED) + element.click() + + def get_subscriptions(self): + _d = self.drv + wait = WebDriverWait(_d , 40) + wait.until( + EC.presence_of_element_located((By.XPATH, StatisticPageLocator.SUBSCRIPTIONS_XPATH) )) + _subscriptions = int( + _d.find_element_by_css_selector(StatisticPageLocator.SUBSCRIPTIONS_CSS).text) + return _subscriptions + + def verify_subscription(self): + _d = self.drv + wait = WebDriverWait(_d , 40) + wait.until(lambda _d: _d.find_elements_by_xpath(StatisticPageLocator.SUBSCRIPTIONS_LIST_XPATH)) + elements_in_subscription = _d.find_elements_by_xpath(StatisticPageLocator.SUBSCRIPTIONS_LIST_XPATH) + return True if len(elements_in_subscription) == self.get_subscriptions() else False + + def get_comments(self): + _d = self.drv + wait = WebDriverWait(_d , 40) + wait.until( EC.presence_of_element_located((By.XPATH, StatisticPageLocator.COMMENTS_XPATH)) ) + _comments = int( + _d.find_element_by_css_selector(StatisticPageLocator.COMMENTS_CSS).text) + return _comments + + def verify_comments(self): + _d = self.drv + wait = WebDriverWait(_d , 40) + if self.get_comments() > 0: + wait.until(lambda _d: _d.find_elements_by_xpath(StatisticPageLocator.COMMENTS_LIST_XPATH)) + elements_in_commented = _d.find_elements_by_xpath(StatisticPageLocator.COMMENTS_LIST_XPATH) + else: elements_in_commented = [] + return True if ((len(elements_in_commented) <= self.get_comments()) + and (len(elements_in_commented) <= 10)) else False + + def get_problems(self): + _d = self.drv + wait = WebDriverWait(_d , 40) + wait.until( + EC.presence_of_element_located((By.XPATH, StatisticPageLocator.PROBLEMS_XPATH))) + _problems = int( + _d.find_element_by_css_selector(StatisticPageLocator.PROBLEMS_CSS).text) + return _problems + + def verify_severities(self): + _d = self.drv + __problems = self.get_problems() + wait = WebDriverWait(_d , 40) + wait.until(lambda _d: _d.find_elements_by_xpath(StatisticPageLocator.IN_SEVERITIES_LIST_XPATH)) + elements_in_severities = _d.find_elements_by_xpath(StatisticPageLocator.IN_SEVERITIES_LIST_XPATH) + return (__problems == len(elements_in_severities)) if __problems <= 10 else (len(elements_in_severities) == 10) + diff --git a/page_object_ecomap/framework/Screenshot.py b/page_object_ecomap/framework/Screenshot.py new file mode 100644 index 00000000..1085eca5 --- /dev/null +++ b/page_object_ecomap/framework/Screenshot.py @@ -0,0 +1,44 @@ +from PIL import Image + + +class Screenshot(object): + def __init__(self, driver): + self.driver = driver + + def get_element_location(self, *locator): + locations = {} + element = self.driver.find_element(*locator) + location = element.location + size = element.size + locations['left'] = location['x'] + locations['top'] = location['y'] + locations['right'] = location['x'] + size['width'] + locations['bottom'] = location['y'] + size['height'] + return locations + + def get_screenshot_as_file(self, file_path): + return self.driver.get_screenshot_as_file(file_path) + + def get_cropped_image(self, path_full_img, path_cropped_img, element_locator): + img_location = self.get_element_location(*element_locator) + self.get_screenshot_as_file(path_full_img) + full_screen = Image.open(path_full_img) + map_img = full_screen.crop( + (img_location['left'], img_location['top'], img_location['right'], img_location['bottom'])) + map_img.save(path_cropped_img) + return map_img + + def get_pixels_by_color(self, mode, color_index, image): + colors = image.convert(mode).getcolors() + for i in range(len(colors)): + if color_index in range(256): + if colors[i][1] == color_index: + return colors[i][0] + return None + + def get_pixels_count(self, image): + width, height = image.size + return width * height + + def get_pixels_percentage(self, num, denum): + return num * 100 / denum diff --git a/page_object_ecomap/framework/__init__.py b/page_object_ecomap/framework/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/page_object_ecomap/tests/.gitignore b/page_object_ecomap/tests/.gitignore new file mode 100644 index 00000000..3e7a5fcd --- /dev/null +++ b/page_object_ecomap/tests/.gitignore @@ -0,0 +1,4 @@ +chromedriver +.idea/ +screen_full.png +screen_cropped.png \ No newline at end of file diff --git a/page_object_ecomap/tests/TestBase.py b/page_object_ecomap/tests/TestBase.py new file mode 100644 index 00000000..f682c9f6 --- /dev/null +++ b/page_object_ecomap/tests/TestBase.py @@ -0,0 +1,27 @@ +import unittest +import os +from selenium import webdriver +from page_object_ecomap.framework.Pages import * +from page_object_ecomap.tests.TestData import TestData + + +class TestBase(unittest.TestCase): + driver = None + + @classmethod + def setUpClass(cls): + cls.test_data = TestData(os.path.dirname(os.path.abspath(__file__)) + "/test_data_file.txt") + base_url = cls.test_data.get("base_url") + cls.path = os.path.dirname(os.path.abspath(__file__)) + "/chromedriver" + cls.driver = webdriver.Chrome(cls.path) + cls.driver.implicitly_wait(40) + cls.driver.set_page_load_timeout(50) + cls.driver.maximize_window() + + cls.home_page = HomePage(cls.driver, base_url) + cls.home_page.open() + + @classmethod + def tearDownClass(cls): + cls.driver.quit() + diff --git a/page_object_ecomap/tests/TestData.py b/page_object_ecomap/tests/TestData.py new file mode 100644 index 00000000..0ae84583 --- /dev/null +++ b/page_object_ecomap/tests/TestData.py @@ -0,0 +1,13 @@ +class TestData: + def __init__(self, file_path): + self.file_path = file_path + + def get(self, keyword): + test_data = "" + with open(self.file_path, 'r') as f: + for line in f: + index = line.find(keyword + ":") + if index != -1: + test_data = line[(index + len(keyword) + 1):] + break + return test_data.strip() diff --git a/page_object_ecomap/tests/TestLocation.py b/page_object_ecomap/tests/TestLocation.py new file mode 100644 index 00000000..396df43c --- /dev/null +++ b/page_object_ecomap/tests/TestLocation.py @@ -0,0 +1,47 @@ +from page_object_ecomap.tests.TestBase import TestBase +import unittest +from page_object_ecomap.framework.Pages import* + + +class testLocation(TestBase): + + @classmethod + def setUpClass(cls): + super(testLocation, cls).setUpClass() + cls.home_page = HomePage(cls.driver) + cls.add_problem = AddProblemPage(cls.driver) + cls.login_page = LoginPage(cls.driver) + cls.home_page.get_login_page() + cls.home_user_page = cls.login_page.login(cls.test_data.get("email"), cls.test_data.get("password")) + + def test_check_location(self): + + # check if "Add problem" tab is present + self.assertTrue(self.home_user_page.get_current_url(), self.home_user_page.get_expected_url()) + self.assertTrue(self.home_user_page.is_add_problem_tab_present()) + + # go to Add problem page + add_problem = self.home_user_page.click_on_add_problem() + self.assertEqual(add_problem.get_current_url(), add_problem.get_expected_url()) + + # get coordinates by application + found_coordinates = self.add_problem.click_on_find_me() + + # get actual coordinates from outside service + actual_coordinates = self.add_problem.get_actual_coordinates() + + self.assertTrue(self.add_problem.check_location(found_coordinates, actual_coordinates), + self.add_problem.get_reason_of_fail()) + + self.assertTrue(self.add_problem.is_location_widget_present()) + + +if __name__ == '__main__': + unittest.main() + + + + + + + diff --git a/page_object_ecomap/tests/TestLoginAsAdmin.py b/page_object_ecomap/tests/TestLoginAsAdmin.py new file mode 100644 index 00000000..ee781501 --- /dev/null +++ b/page_object_ecomap/tests/TestLoginAsAdmin.py @@ -0,0 +1,22 @@ +from page_object_ecomap.tests.TestBase import TestBase +import unittest +from page_object_ecomap.framework.Pages import * + + +class TestLoginAsAdmin(TestBase): + + def test_1_assert_home_page_is_open(self): + self.assertEqual(self.home_page.get_current_url(), self.home_page.get_expected_url()) + self.assertTrue(self.home_page.is_element_present(*HomePageLocator.LOG_IN)) + + def test_2_log_in_as_admin_and_assert_success(self): + login_page = self.home_page.get_login_page() + self.assertTrue(login_page.is_element_present(*LoginPageLocator.EMAIL)) + self.assertTrue(login_page.is_element_present(*LoginPageLocator.PASSWORD)) + self.assertTrue(login_page.is_element_present(*LoginPageLocator.SUBMIT)) + home_user_page = login_page.login(self.test_data.get("email"), self.test_data.get("password")) + self.assertTrue(home_user_page.is_logout_btn_present()) + self.assertTrue(home_user_page.is_user_profile_link_present()) + +if __name__ == '__main__': + unittest.main() diff --git a/page_object_ecomap/tests/TestMap.py b/page_object_ecomap/tests/TestMap.py new file mode 100644 index 00000000..8c7752b2 --- /dev/null +++ b/page_object_ecomap/tests/TestMap.py @@ -0,0 +1,30 @@ +from page_object_ecomap.tests.TestBase import TestBase +from page_object_ecomap.framework.Pages import * +from page_object_ecomap.framework.Screenshot import Screenshot +import unittest + + +class TestMap(TestBase): + + def test_full_map(self): + screenshot = Screenshot(self.driver) + self.assertTrue(self.home_page.is_element_present(*MapLocator.MAP), "Map element is not present on Home page") + map_img = screenshot.get_cropped_image("screen_full.png", "screen_cropped.png", MapLocator.MAP) + grey_pixels = screenshot.get_pixels_by_color('L', 227, map_img) + white_pixels = screenshot.get_pixels_by_color('L', 255, map_img) + all_pixels = screenshot.get_pixels_count(map_img) + grey_percent = screenshot.get_pixels_percentage(grey_pixels, all_pixels) + white_percent = screenshot.get_pixels_percentage(white_pixels, all_pixels) + self.assertLess(grey_percent, 10, 'Map isn''t displayed properly (too much of grey color)') + self.assertLess(white_percent, 10, 'Map isn''t displayed properly (too much of white color)') + + +class TestMapLoggedIn(TestMap): + + def setUp(self): + home_user_page = HomePage(self.driver).get_login_page().login( + self.test_data.get("email"), self.test_data.get("password")) + self.assertTrue(home_user_page.is_logout_btn_present()) + +if __name__ == '__main__': + unittest.main() diff --git a/page_object_ecomap/tests/TestRegistration.py b/page_object_ecomap/tests/TestRegistration.py new file mode 100644 index 00000000..ea86333c --- /dev/null +++ b/page_object_ecomap/tests/TestRegistration.py @@ -0,0 +1,43 @@ +import random +import unittest +from page_object_ecomap.framework.Locators import RegisterPageLocator +from page_object_ecomap.tests.TestBase import TestBase, HomeUserPage, Registration + + +class TestRegistration(TestBase): + @classmethod + def setUpClass(cls): + super(TestRegistration, cls).setUpClass() + cls.user_page = HomeUserPage(cls.driver) + cls.reg_page = Registration(cls.driver) + cls.home_page.get_registration_page() + + def test_1_check_registration_page(self): + self.assertEqual(self.reg_page.get_current_url(), self.reg_page.get_expected_reg_url()) + self.assertTrue(self.reg_page.is_element_present(*RegisterPageLocator.REG_BLOCK)) + self.assertTrue(self.reg_page.is_element_present(*RegisterPageLocator.EMAIL)) + self.assertTrue(self.reg_page.is_element_present(*RegisterPageLocator.NAME)) + self.assertTrue(self.reg_page.is_element_present(*RegisterPageLocator.SURNAME)) + self.assertTrue(self.reg_page.is_element_present(*RegisterPageLocator.NICKNAME)) + self.assertTrue(self.reg_page.is_element_present(*RegisterPageLocator.PASSWORD)) + self.assertTrue(self.reg_page.is_element_present(*RegisterPageLocator.CONFIRMPASSWORD)) + self.assertTrue(self.reg_page.is_element_present(*RegisterPageLocator.SUBMIT_BUTTON)) + + def test_2_check_registered_user_page(self): + self.reg_page.reg(self.test_data.get("registration_email") % self.generate_random_email(), + self.test_data.get("registration_name"), + self.test_data.get("registration_surname"), + self.test_data.get("registration_nickname") % self.generate_random_email(), + self.test_data.get("registration_password"), + self.test_data.get("registration_confirmpassword")) + self.reg_page.wait_linked_text_changed() + user_name = self.test_data.get("registration_name") + " " + self.test_data.get("registration_surname") + att = self.user_page.user_credentials_btn_is_present() + self.assertEqual(user_name.upper(), att) + self.assertTrue(self.user_page.is_logout_btn_present()) + + def generate_random_email(self): + return str(random.randint(1, 1000)) + +if __name__ == '__main__': + unittest.main() diff --git a/page_object_ecomap/tests/TestStatistic.py b/page_object_ecomap/tests/TestStatistic.py new file mode 100644 index 00000000..2b90ab1e --- /dev/null +++ b/page_object_ecomap/tests/TestStatistic.py @@ -0,0 +1,34 @@ +from page_object_ecomap.tests.TestBase import TestBase +import unittest +from selenium import webdriver +from page_object_ecomap.framework.Pages import * + +class TestStatistic(TestBase): + + @classmethod + def setUpClass(cls): + cls.browser = webdriver.Firefox(firefox_binary="/usr/bin/firefox", + executable_path="/home/svyshnevskyy/newdisk/geckodriver/geckodriver") + cls.browser.maximize_window() + cls.browser.get("http://localhost/#/map") + cls.statistic_page = StatisticPage(cls.browser ) + + def test_1_assert_statistic_page_is_open(self): + self.statistic_page.set_driver(self.browser) + self.statistic_page.goToStatisticPage() + self.assertEqual(self.statistic_page.get_expected_url(), self.statistic_page.get_current_url()) + + def test_2_assert_valid_stat_in_all_top(self): + + self.assertTrue(self.statistic_page.verify_subscription()) + self.assertTrue(self.statistic_page.verify_severities()) + self.assertTrue(self.statistic_page.verify_comments()) + + + @classmethod + def tearDownClass(cls): + cls.browser.quit() + + +if __name__ == '__main__': + unittest.main() diff --git a/page_object_ecomap/tests/TestUserChangePassword.py b/page_object_ecomap/tests/TestUserChangePassword.py new file mode 100644 index 00000000..de8c7c18 --- /dev/null +++ b/page_object_ecomap/tests/TestUserChangePassword.py @@ -0,0 +1,38 @@ +import unittest +import random +from page_object_ecomap.tests.TestBase import TestBase +from page_object_ecomap.framework.Pages import * + + +class TestUserChangePassword(TestBase): + + @classmethod + def setUpClass(cls): + super(TestUserChangePassword, cls).setUpClass() + cls.home_page = HomePage(cls.driver) + cls.user_page = HomeUserPage(cls.driver) + cls.login_page = LoginPage(cls.driver) + cls.user_profile_page = UserProfilePage(cls.driver) + registration_page = cls.home_page.get_registration_page() + registration_page.reg(cls.test_data.get("registration_email") % cls.generate_random_email(), + cls.test_data.get("registration_name"), + cls.test_data.get("registration_surname"), + cls.test_data.get("registration_nickname") % cls.generate_random_email(), + cls.test_data.get("change_password_old_pass"), + cls.test_data.get("change_password_old_pass")) + + + def test_user_change_pass(self): + self.user_profile_page = self.home_page.get_user_profile_page() + self.user_profile_page.change_pwd(self.test_data.get("change_password_old_pass"), + self.test_data.get("change_password_new_pass"), + self.test_data.get("change_password_new_pass_repeat")) + + self.assertTrue(self.user_profile_page.is_success_popup_present()) + + @staticmethod + def generate_random_email(): + return str(random.randint(1, 100000)) + +if __name__ == '__main__': + unittest.main() diff --git a/page_object_ecomap/tests/__init__.py b/page_object_ecomap/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/page_object_ecomap/tests/test_data_file.txt b/page_object_ecomap/tests/test_data_file.txt new file mode 100644 index 00000000..6b862729 --- /dev/null +++ b/page_object_ecomap/tests/test_data_file.txt @@ -0,0 +1,17 @@ +base_url: http://localhost/#/map +email: admin@ecomap.com +password: secre! +registration_email: user%s@ukr.net +registration_name: iamuser +registration_surname: ecomap +registration_nickname: eco%s +registration_password: qwerty +registration_confirmpassword: qwerty +email_for_change_pass: qqq@qqq.com +pass_for_change_pass: qqqqqq +change_password_email: qqq@qqq.com +change_password_password: qqqqqq +change_password_old_pass: qqqqqq +change_password_new_pass: qqqqqq +change_password_new_pass_repeat: qqqqqq +change_password_repeat_wrong: 654321 diff --git a/requirements.txt b/requirements.txt index 2ae20a71..3b6deb3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,27 +8,18 @@ Flask-Triangle==0.5.4 Jinja2==2.8 MarkupSafe==0.23 MySQL-python==1.2.5 -PAM==0.4.2 Pillow==2.3.0 Pygments==2.1.1 Sphinx==1.3.5 -Twisted-Core==13.2.0 -Twisted-Web==13.2.0 Werkzeug==0.11.3 -adium-theme-ubuntu==0.3.4 alabaster==0.7.7 -apt-xapian-index==0.45 argparse==1.2.1 astroid==1.0.1 -chardet==2.0.1 +chardet==2.3.0 colorama==0.2.5 -command-not-found==0.3 coverage==4.0.3 -debtagshw==0.1 -defer==1.0.6 -dirspec==13.10 +defer==1.0.4 docutils==0.11 -duplicity==0.6.23 flask-memcache-session==2.0 funcsigs==0.4 functools32==3.2.3-2 @@ -42,39 +33,27 @@ logilab-common==0.61.0 lxml==3.3.3 mock==1.3.0 oauthlib==0.6.1 -oneconf==0.3.7.14.04.1 pbr==1.8.1 pexpect==3.1 piston-mini-client==0.7.5 -pyOpenSSL==0.13 +pyOpenSSL==16.2.0 pycrypto==2.6.1 -pycups==1.9.66 -pygobject==3.12.0 pylint==1.1.0 pyserial==2.6 -pysmbc==1.0.14.1 -python-apt==0.9.3.5ubuntu2 -python-debian==0.1.21-nmu2ubuntu2 python-memcached==1.57 pytz==2015.7 pyxdg==0.25 reportlab==3.0 requests==2.2.1 roman==2.0.0 -sessioninstaller==0.0.0 six==1.10.0 snowballstemmer==1.2.1 -software-center-aptd-plugins==0.0.0 sphinx-rtd-theme==0.1.9 sphinxcontrib-httpdomain==1.1.8 --e git://github.com/michaeljones/sphinx-to-github.git@0a46518fe01e0c489a82ce95d16f20a0a2c3fc1b#egg=sphinxtogithub-master -system-service==0.1.6 traceback2==1.4.0 unittest2==1.1.0 -unity-lens-photos==1.0 urllib3==1.7.1 -vboxapi==1.0 wheel==0.24.0 wsgiref==0.1.2 -xdiagnose==3.6.3build2 zope.interface==4.0.5 +python-magic==0.4.13