<repo_name>viorelaioia-zz/discourse-tests<file_sep>/pages/category.py
from selenium.webdriver.common.by import By

from pages.base import Base


class Category(Base):

    _notifications_dropdown_locator = (By.CSS_SELECTOR, '.dropdown-toggle')
    _notification_options_locator = (By.CSS_SELECTOR, '.dropdown-menu li span[class="title"]')
    _options_locator = (By.CSS_SELECTOR, '.dropdown-menu li a')

    @property
    def options(self):
        options_list = self.selenium.find_elements(*self._notification_options_locator)
        return [option.text for option in options_list]

    def click_notifications_dropdown(self):
        self.selenium.find_element(*self._notifications_dropdown_locator).click()

    def select_option(self, option):
        notification_option_index = self.options.index(option)
        notification_options_list = self.selenium.find_elements(*self._options_locator)
        notification_options_list[notification_option_index].click()
<file_sep>/README.md
# discourse-tests
Automated tests for discourse.mozilla-community.org

How to set up and run Discourse tests locally

This repository contains Selenium tests used to test:
* staging: https://discourse.staging.paas.mozilla.community/
* production: https://discourse.mozilla-community.org/

##You will need to install the following:
#### Git
If you have cloned this project already then you can skip this! GitHub has excellent guides for Windows, Mac OS X, and Linux.

#### Python
Before you will be able to run these tests you will need to have Python 2.6.8+ installed.

##Running tests locally
Some of the tests in discourse-tests require accounts for https://discourse.staging.paas.mozilla.community.  You'll need to create two sets of credentials with varying privilege levels.

1. Create two username and password combinations on https://mozillians.org
2. Make sure one of the accounts is vouched 3 times
3. Create the same Two accounts used in step #1 in https://discourse.staging.paas.mozilla.community
4. Copy discourse-tests/variables.json to a location outside of discourse-tests. Update the 'vouched' and 'unvouched' users in variables.json with those credentials

* [Install Tox](https://tox.readthedocs.io/en/latest/install.html)
* Run PYTEST_ADDOPTS="--variables=/path/to/variables.json" tox
<file_sep>/pages/register.py
from selenium.webdriver.common.by import By

from pages.base import Base
from pages.home_page import Home


class Register(Base):
    _username_locator = (By.ID, 'new-account-username')
    _name_locator = (By.ID, 'new-account-name')
    _create_new_account_button_locator = (By.CSS_SELECTOR, '.modal-footer .btn-primary')

    def enter_username(self, username):
        self.selenium.find_element(*self._username_locator).clear()
        self.selenium.find_element(*self._username_locator).send_keys(username)

    def click_create_new_account_button(self):
        self.selenium.find_element(*self._create_new_account_button_locator).click()
        return Home(self.base_url, self.selenium)
<file_sep>/tests/conftest.py
import uuid

import pyotp
import pytest

import restmail


@pytest.fixture
def capabilities(request, capabilities):
    driver = request.config.getoption('driver')
    if capabilities.get('browserName', driver).lower() == 'firefox':
        capabilities['marionette'] = True
    return capabilities


@pytest.fixture
def selenium(selenium):
    selenium.implicitly_wait(10)
    selenium.maximize_window()
    return selenium


@pytest.fixture
def stored_users(variables):
    return variables['users']


@pytest.fixture
def vouched_user(stored_users):
    return stored_users['vouched']


@pytest.fixture
def unvouched_user(stored_users):
    return stored_users['unvouched']


@pytest.fixture
def new_email():
    return '<EMAIL>'.format(uuid.uuid1())


@pytest.fixture
def new_user(new_email):
    return {'email': new_email}


@pytest.fixture
def login_link(username):
    mail = restmail.get_mail(username)
    mail_content = mail[0]['text'].replace('\n', ' ').replace('amp;', '').split(" ")
    for link in mail_content:
        if link.startswith("https"):
            return link


@pytest.fixture
def ldap_user(stored_users):
    return stored_users['ldap']


@pytest.fixture
def passcode(secret_seed):
    totp = pyotp.TOTP(secret_seed)
    return totp.now()
<file_sep>/pages/base.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait

from pages.auth0 import Auth0
from pages.page import Page
from tests import conftest


class Base(Page):
    _login_button_locator = (By.CSS_SELECTOR, 'button.login-button')
    _avatar_image_locator = (By.CSS_SELECTOR, '#current-user a[class="icon"]')
    _logout_button_locator = (By.CSS_SELECTOR, '.widget-link.logout')
    _page_not_found_error_message_locator = (By.CSS_SELECTOR, '.page-not-found')
    _category_locator = (By.CSS_SELECTOR, '.category-navigation .category-breadcrumb li:nth-child(1) a[aria-label="Display category list"]')
    _subcategory_locator = (By.CSS_SELECTOR, '.category-navigation .category-breadcrumb li:nth-child(2) a[aria-label="Display category list"]')
    _preferences_button_locator = (By.CSS_SELECTOR, '.user-preferences-link')
    _toggle_menu_button_locator = (By.ID, 'toggle-hamburger-menu')
    _categories_list = (By.CSS_SELECTOR, 'li[class*="category-link"] a:nth-child(1)')

    @property
    def page_title(self):
        WebDriverWait(self.selenium, self.timeout).until(lambda s: self.selenium.title)
        return self.selenium.title

    @property
    def is_login_button_displayed(self):
        return self.is_element_visible(*self._login_button_locator)

    @property
    def is_avatar_displayed(self):
        return self.is_element_visible(*self._avatar_image_locator)

    @property
    def page_not_found_error_message(self):
        self.wait_for_element_visible(*self._page_not_found_error_message_locator)
        return self.selenium.find_element(*self._page_not_found_error_message_locator).text

    @property
    def subcategory(self):
        return self.selenium.find_element(*self._subcategory_locator).text

    @property
    def categories_list_names(self):
        categories_list = self.selenium.find_elements(*self._categories_list)
        return [category.text for category in categories_list]

    @property
    def category(self):
        return self.selenium.find_element(*self._category_locator).text

    def click_sign_in_button(self):
        self.selenium.find_element(*self._login_button_locator).click()

    def login(self, email):
        self.click_sign_in_button()
        auth0 = Auth0(self.base_url, self.selenium)
        auth0.request_login_link(email)
        login_link = conftest.login_link(email)
        self.selenium.get(login_link)

    def login_with_ldap(self, email_address, password, passcode):
        self.click_sign_in_button()
        auth0 = Auth0(self.base_url, self.selenium)
        auth0.login_with_ldap(email_address, password, passcode)

    def click_avatar(self):
        self.selenium.find_element(*self._avatar_image_locator).click()
        WebDriverWait(self.selenium, self.timeout).until(
            lambda s: self.selenium.find_element(*self._logout_button_locator))

    def click_logout_menu_item(self):
        self.click_avatar()
        self.selenium.find_element(*self._logout_button_locator).click()
        WebDriverWait(self.selenium, self.timeout).until(lambda s: self.is_login_button_displayed)

    def click_preferences(self):
        self.click_avatar()
        self.selenium.find_element(*self._preferences_button_locator).click()
        from pages.preferences import Preferences
        return Preferences(self.base_url, self.selenium)

    def create_new_user(self, email):
        self.login(email)
        from pages.register import Register
        return Register(self.base_url, self.selenium)

    def click_toggle_menu(self):
        self.selenium.find_element(*self._toggle_menu_button_locator).click()

    def select_category(self, category):
        categories_list = self.selenium.find_elements(*self._categories_list)
        for item in categories_list:
            if item.text == category:
                item.click()
                from pages.category import Category
                return Category(self.base_url, self.selenium)
<file_sep>/pages/auth0.py
from selenium.webdriver.common.by import By

from pages.page import Page


class Auth0(Page):

    _login_with_email_button_locator = (By.CSS_SELECTOR, '.auth0-lock-passwordless-button.auth0-lock-passwordless-big-button')
    _email_input_locator = (By.CSS_SELECTOR, '.auth0-lock-passwordless-pane>div>div>input')
    _send_email_button_locator = (By.CSS_SELECTOR, '.auth0-lock-passwordless-submit')

    _login_with_ldap_button_locator = (By.CSS_SELECTOR, '.auth0-lock-ldap-button.auth0-lock-ldap-big-button')
    _ldap_email_field_locator = (By.CSS_SELECTOR, '.auth0-lock-input-email .auth0-lock-input')
    _ldap_password_field_locator = (By.CSS_SELECTOR, '.auth0-lock-input-password .auth0-lock-input')
    _login_button_locator = (By.CSS_SELECTOR, '.auth0-lock-submit')

    _enter_passcode_button = (By.CSS_SELECTOR, '.passcode-label .positive.auth-button')
    _passcode_field_locator = (By.CSS_SELECTOR, '.passcode-label input[name="passcode"]')
    _duo_iframe_locator = (By.ID, 'duo_iframe')

    def request_login_link(self, username):
        self.wait_for_element_visible(*self._login_with_email_button_locator)
        self.selenium.find_element(*self._login_with_email_button_locator).click()
        self.wait_for_element_visible(*self._email_input_locator)
        self.selenium.find_element(*self._email_input_locator).send_keys(username)
        self.selenium.find_element(*self._send_email_button_locator).click()

    def login_with_ldap(self, email, password, passcode):
        self.wait_for_element_visible(*self._login_with_ldap_button_locator)
        self.selenium.find_element(*self._login_with_ldap_button_locator).click()
        self.selenium.find_element(*self._ldap_email_field_locator).send_keys(email)
        self.selenium.find_element(*self._ldap_password_field_locator).send_keys(password)
        self.selenium.find_element(*self._login_button_locator).click()
        self.enter_passcode(passcode)

    def enter_passcode(self, passcode):
        self.selenium.switch_to_frame('duo_iframe')
        self.wait_for_element_visible(*self._enter_passcode_button)
        self.selenium.find_element(*self._enter_passcode_button).click()
        self.selenium.find_element(*self._passcode_field_locator).send_keys(passcode)
        self.selenium.find_element(*self._enter_passcode_button).click()
        self.selenium.switch_to_default_content()
<file_sep>/pages/home_page.py
from selenium.webdriver.common.by import By

from pages.base import Base
from pages.topic_page import TopicPage


class Home(Base):
    _create_new_topic_button_locator = (By.ID, 'create-topic')
    _new_title_topic_locator = (By.ID, 'reply-title')
    _category_combobox_locator = (By.CSS_SELECTOR, '.category-input div[class*="category-combobox"]')
    _category_input_locator = (By.CSS_SELECTOR, '.select2-search .select2-input')
    _search_results_locator = (By.CSS_SELECTOR, '.select2-results .badge-wrapper.bar')
    _topic_description_locator = (By.CSS_SELECTOR, 'div[class*="textarea-wrapper"] textarea')
    _create_topic_button_locator = (By.CSS_SELECTOR, '.submit-panel button')
    _similar_topics_window_locator = (By.CSS_SELECTOR, '.composer-popup.hidden.similar-topics')
    _close_similar_topics_window_button = (By.CSS_SELECTOR, '.composer-popup.hidden.similar-topics .close')

    def __init__(self, base_url, selenium, open_url=True):
        Base.__init__(self, base_url, selenium)
        if open_url:
            self.selenium.get(self.base_url)

    def click_add_new_topic(self):
        self.selenium.find_element(*self._create_new_topic_button_locator).click()

    def enter_new_topic_title(self, title):
        self.wait_for_element_visible(*self._new_title_topic_locator)
        self.selenium.find_element(*self._new_title_topic_locator).send_keys(title)

    def select_category(self, category):
        self.selenium.find_element(*self._category_combobox_locator).click()
        self.wait_for_element_visible(*self._category_input_locator)
        self.selenium.find_element(*self._category_input_locator).send_keys(category)
        search_results = self.selenium.find_elements(*self._search_results_locator)
        for item in search_results:
            if item.text == category:
                item.click()

    def enter_topic_description(self, description):
        self.selenium.find_element(*self._topic_description_locator).send_keys(description)
        self.wait_for_element_visible(*self._similar_topics_window_locator)
        self.selenium.find_element(*self._close_similar_topics_window_button).click()

    def click_create_topic(self):
        self.selenium.find_element(*self._create_topic_button_locator).click()
        return TopicPage(self.base_url, self.selenium)
<file_sep>/tests/test_account.py
# coding=utf-8
import random
import uuid

import pytest

from pages.home_page import Home
from tests import conftest


class TestAccount:

    @pytest.mark.nondestructive
    def test_login_logout(self, base_url, selenium, unvouched_user):
        home_page = Home(base_url, selenium)
        home_page.login(unvouched_user['email'])
        assert home_page.is_avatar_displayed
        home_page.click_logout_menu_item()
        assert home_page.is_login_button_displayed

    @pytest.mark.nondestructive
    def test_unvouched_mozillian_cannot_access_private_categories(self, base_url, selenium, unvouched_user):
        home_page = Home(base_url, selenium)
        home_page.login(unvouched_user['email'])
        assert home_page.is_avatar_displayed
        home_page.go_to_url('https://discourse.mozilla-community.org/c/mozillians/vouched-mozillians')
        error_message = 'Oops! That page doesn’t exist or is private.'.decode('utf8')
        assert error_message == home_page.page_not_found_error_message
        home_page.go_to_url('https://discourse.mozilla-community.org/c/mozillians/nda')
        assert error_message == home_page.page_not_found_error_message

    @pytest.mark.nondestructive
    def test_vouched_mozillian_can_access_private_category(self, base_url, selenium, vouched_user):
        home_page = Home(base_url, selenium)
        home_page.login(vouched_user['email'])
        assert home_page.is_avatar_displayed
        home_page.go_to_url('https://discourse.mozilla-community.org/c/mozillians/vouched-mozillians')
        assert 'Vouched Mozillians' == home_page.subcategory
        home_page.click_logout_menu_item()

    @pytest.mark.nondestructive
    def test_create_and_delete_account(self, base_url, selenium, new_user):
        home_page = Home(base_url, selenium)
        register = home_page.create_new_user(new_user['email'])
        register.enter_username("test_user")
        home_page = register.click_create_new_account_button()
        assert home_page.is_avatar_displayed
        preferences = home_page.click_preferences()
        home_page = preferences.account.delete_account()
        assert home_page.is_login_button_displayed

    @pytest.mark.nondestructive
    def test_user_can_watch_category(self, base_url, selenium, unvouched_user):
        home_page = Home(base_url, selenium)
        home_page.login(unvouched_user['email'])
        assert home_page.is_avatar_displayed
        home_page.click_toggle_menu()
        categories = home_page.categories_list_names
        category = random.choice(categories)
        category_page = home_page.select_category(category)
        category_page.click_notifications_dropdown()
        category_page.select_option("Watching")
        preferences = category_page.click_preferences()
        preferences_category_page = preferences.categories
        assert category in preferences_category_page.watched_categories
        preferences_category_page.remove_category(category)
        preferences_category_page.click_save_button()
        preferences.click_logout_menu_item()
        assert home_page.is_login_button_displayed

    @pytest.mark.nondestructive
    def test_user_can_go_through_existing_categories(self, base_url, selenium, vouched_user):
        home_page = Home(base_url, selenium)
        home_page.login(vouched_user['email'])
        assert home_page.is_avatar_displayed
        home_page.click_toggle_menu()
        categories_list = home_page.categories_list_names
        for category_name in categories_list:
            index = categories_list.index(category_name)
            home_page.select_category(categories_list[index])
            assert home_page.category == category_name
            home_page.click_toggle_menu()
        home_page.click_logout_menu_item()

    @pytest.mark.nondestructive
    def test_user_can_create_a_new_topic(self, base_url, selenium, ldap_user):
        home_page = Home(base_url, selenium)
        home_page.login_with_ldap(ldap_user['email'], ldap_user['password'], conftest.passcode(ldap_user['secret_seed']))
        assert home_page.is_avatar_displayed
        home_page.click_add_new_topic()
        title = "Test topic - {0}".format(uuid.uuid1())
        home_page.enter_new_topic_title(title)
        category = "playground"
        home_page.select_category(category)
        description = "This is a topic description for testing - {}".format(uuid.uuid1())
        home_page.enter_topic_description(description)
        topic_page = home_page.click_create_topic()
        assert title == topic_page.topic_title
        assert category == topic_page.topic_category
        assert description == topic_page.topic_description
        topic_page.click_logout_menu_item()
        assert topic_page.is_login_button_displayed
<file_sep>/pages/preferences.py
from selenium.webdriver.common.by import By

from pages.base import Base
from pages.page import PageRegion


class Preferences(Base):
    _account_button_locator = (By.CSS_SELECTOR, 'li[class*="nav-account"] a')
    _categories_button_locator = (By.CSS_SELECTOR, 'li[class*="no-glyph indent nav-categories"] a')
    _user_preferences_section_locator = (By.CSS_SELECTOR, '.user-right.user-preferences')

    @property
    def account(self):
        self.selenium.find_element(*self._account_button_locator).click()
        return self.Account(self.base_url, self.selenium,
                            self.selenium.find_element(*self._user_preferences_section_locator))

    @property
    def categories(self):
        self.selenium.find_element(*self._categories_button_locator).click()
        return self.Category(self.base_url, self.selenium,
                             self.selenium.find_element(*self._user_preferences_section_locator))

    class Account(PageRegion):
        _delete_account_button_locator = (By.CSS_SELECTOR, '.delete-account button')
        _delete_account_confirmation_locator = (By.CSS_SELECTOR, '.modal-footer .btn-danger')

        def delete_account(self):
            self.selenium.find_element(*self._delete_account_button_locator).click()
            self.wait_for_element_visible(*self._delete_account_confirmation_locator)
            self.selenium.find_element(*self._delete_account_confirmation_locator).click()
            from pages.home_page import Home
            return Home(self.base_url, self.selenium)

    class Category(PageRegion):
        _watched_categories_locator = (By.CSS_SELECTOR, 'div[class*="category-controls"]:first-of-type div[class="item"]')
        _delete_category_buttons_locator = (By.CSS_SELECTOR, 'div[class*="category-controls"]:first-of-type a[class="remove"]')
        _save_button_locator = (By.CSS_SELECTOR, 'button[class*="save-user"]')

        @property
        def watched_categories(self):
            watched_categories = self.selenium.find_elements(*self._watched_categories_locator)
            return [category.text for category in watched_categories]

        def remove_category(self, category):
            category_index = self.watched_categories.index(category)
            delete_watched_categories_buttons = self.selenium.find_elements(*self._delete_category_buttons_locator)
            delete_watched_categories_buttons[category_index].click()

        def click_save_button(self):
            self.selenium.find_element(*self._save_button_locator).click()
<file_sep>/pages/topic_page.py
from selenium.webdriver.common.by import By

from pages.base import Base


class TopicPage(Base):
    _topic_title_locator = (By.CSS_SELECTOR, '.fancy-title')
    _topic_category_locator = (By.CSS_SELECTOR, '.title-wrapper .badge-wrapper.bar')
    _topic_description_locator = (By.CSS_SELECTOR, '.regular.contents p')

    @property
    def topic_title(self):
        return self.selenium.find_element(*self._topic_title_locator).text

    @property
    def topic_category(self):
        return self.selenium.find_element(*self._topic_category_locator).text

    @property
    def topic_description(self):
        return self.selenium.find_element(*self._topic_description_locator).text
