Skip to content
This repository has been archived by the owner on Aug 20, 2018. It is now read-only.

address #73 #80

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 5 additions & 3 deletions pages/base_test.py
Expand Up @@ -82,16 +82,18 @@ def create_run(self, mozwebqa, activate=False, product=None, version=None, suite
if activate:
manage_runs_pg = MozTrapManageRunsPage(mozwebqa)
manage_runs_pg.filter_runs_by_name(name=run['name'])
manage_runs_pg.activate_run(name=run['name'])

runs = manage_runs_pg.get_runs
runs[0].activate()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

return run

def delete_run(self, mozwebqa, run, delete_version=False, delete_product=False):
manage_runs_pg = MozTrapManageRunsPage(mozwebqa)

manage_runs_pg.go_to_manage_runs_page()
manage_runs_pg.filter_runs_by_name(name=run['name'])
manage_runs_pg.delete_run(name=run['name'])
runs = manage_runs_pg.get_runs
runs[0].delete()

if delete_version:
self.delete_version(mozwebqa, version=run['version'], delete_product=delete_product)
Expand Down
199 changes: 172 additions & 27 deletions pages/create_run_page.py
Expand Up @@ -9,30 +9,186 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

from pages.page import Page
from pages.base_page import MozTrapBasePage


class MozTrapCreateRunPage(MozTrapBasePage):

_page_title = 'MozTrap'

class MozTrapEditRunPage(MozTrapBasePage):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1

_known_kwargs = ['name', 'desc', 'start_date', 'end_date', 'suite_list']
_product_version_readonly_locator = (By.CSS_SELECTOR, 'div.formfield.product-version-field.readonly > span')
_name_locator = (By.ID, 'id_name')
_product_version_select_locator = (By.ID, 'id_productversion')
_description_locator = (By.ID, 'id_description')
_start_date_locator = (By.ID, 'id_start')
_end_date_locator = (By.ID, 'id_end')
_suite_select_locator = (By.CSS_SELECTOR, '#run-add-form .multiunselected .itemlist article.selectitem[data-title="%(suite_name)s"] input.bulk-value')
_include_selected_suites_locator = (By.CSS_SELECTOR, '#run-add-form .multiselect .include-exclude .action-include')
_available_suites_locator = (By.CSS_SELECTOR, '.multiunselected .itemlist article.selectitem')
_included_suites_locator = (By.CSS_SELECTOR, '.multiselected .itemlist article.selectitem')
_include_selected_suites_locator = (By.CSS_SELECTOR, '.multiselect .include-exclude .action-include')
_remove_selected_suites_locator = (By.CSS_SELECTOR, '.multiselect .include-exclude .action-exclude')
_submit_locator = (By.CSS_SELECTOR, '#run-add-form .form-actions > button')

# Note on __init__: it fails with 'ReferenceError: jQuery is not defined'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incidentally a dev for Socorro recently updated this method in socorro-tests to check for jQuery's existence and that .active == 0 to work around problems where on pages where jQuery might not exist.

# if you try to wait_for_ajax

def fill_fields(self, **kwargs):
'''
::keyword args::
name
desc
start_date
end_date
suite_list
'''

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line contains whitespace

for key in kwargs.keys():
if not key in self._known_kwargs:
raise Exception("%s unrecognized, use only recognized kwargs:\n%s" %
(key, "\n".join(self._known_kwargs)))

if kwargs.has_key('name'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.has_key() is deprecated, use 'in'

name_field = self.selenium.find_element(*self._name_locator)
name_field.send_keys(kwargs['name'])
if kwargs.has_key('desc'):
self.selenium.find_element(*self._description_locator).send_keys(kwargs['desc'])
if kwargs.has_key('start_date'):
self.type_in_element(self._start_date_locator, kwargs['start_date'])
if kwargs.has_key('end_date'):
self.selenium.find_element(*self._end_date_locator).send_keys(kwargs['end_date'])

if kwargs.has_key('suite_list') and kwargs['suite_list']:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am trying to decide if this section is overly complicated (remove unwanted from include, add wanted from available, check to make sure there are no wanteds that aren't available). none of the tests actually test the removal of suites from runs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree it's probably not safe to assume that suites should be deselected if they're not in the included list.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An API for inserting test data would be so beneficial for this project!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# remove unwanted
for suite in self.included_suites:
print 'included has %s' % suite.name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should delete this print and the ones below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i've moved most of the prints, but i left one that was very very useful in debugging.

if not suite.name in kwargs['suite_list']:
print 'selecting for removal'
suite.select()
self.remove_selected_suites()
# add wanted
for suite in self.available_suites:
print 'available has %s' % suite.name
if suite.name in kwargs['suite_list']:
print 'selecting for addition'
suite.select()
self.add_selected_suites()
# check for strays
included_suite_names = self.included_suite_names
for suite_name in kwargs['suite_list']:
if not suite_name in included_suite_names:
raise Exception('suite %s not found' % suite_name)

self.selenium.find_element(*self._submit_locator).click()

@property
def product_version(self):
return self.selenium.find_element(*self._product_version_readonly_locator).text

@property
def included_suites(self):
'''this method only works if run is in draft mode.'''
suites = self.selenium.find_elements(*self._included_suites_locator)
included_suites = [self.Suite(self.testsetup, loc) for loc in suites]
print 'included_suites:\n%s' % included_suites
return included_suites

@property
def included_suite_names(self):
# XXX fix this so that it returns something when run is active
included_suite_names = [suite.name for suite in self.included_suites]
print 'included_suite_names:\n%s' % included_suite_names
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove this print.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @teodosia
this is the print i left behind for debugging purposes (if the list is empty when you expect it to be full, you probably have an issue with FF version vs selenium version).

return included_suite_names

def remove_selected_suites(self):
'''this method only works if run is in draft mode.'''
self.selenium.find_element(*self._remove_selected_suites_locator).click()

@property
def available_suites(self):
'''this method only works if run is in draft mode.'''
suites = self.selenium.find_elements(*self._available_suites_locator)
return [self.Suite(self.testsetup, loc) for loc in suites]

@property
def available_suite_names(self):
'''this method only works if run is in draft mode.'''
return [suite.name for suite in self.available_suites]

def add_selected_suites(self):
'''this method only works if run is in draft mode.'''
self.selenium.find_element(*self._include_selected_suites_locator).click()

class Suite(Page):
_name_locator = (By.CSS_SELECTOR, 'div.name > h5.title')
_checkbox_locator = (By.CSS_SELECTOR, 'label.bulk-type')
_drag_locator = (By.CSS_SELECTOR, )

def __init__(self, testsetup, webelement):
Page.__init__(self, testsetup)
self.webelement = webelement

@property
def name(self):
return self.webelement.find_element(*self._name_locator).text

@property
def position(self):
pass

def select(self):
self.webelement.find_element(*self._checkbox_locator).click()

def drag_to(self, position):
pass


class MozTrapCreateRunPage(MozTrapEditRunPage):

_page_title = 'MozTrap'

_product_version_select_locator = (By.ID, 'id_productversion')
_run_manage_locator = (By.CSS_SELECTOR, '#manageruns .itemlist .listitem .title[title="%(run_name)s"]')
_run_homepage_locator = (By.CSS_SELECTOR, '.runsdrill .runsfinder .runs .colcontent .title[title="%(run_name)s"]')
_run_tests_button_locator = (By.CSS_SELECTOR, 'div.form-actions > button')

def __init__(self, testsetup):
MozTrapEditRunPage.__init__(self, testsetup)
self._known_kwargs.append('product_version')

def go_to_create_run_page(self):
self.selenium.get(self.base_url + '/manage/run/add/')
self.is_the_current_page

def create_run(self, name='Test Run', product_version='Test Product Test Version', desc='This is a test run', start_date='2011-01-01', end_date='2012-12-31', suite_list=None):
def fill_fields(self, **kwargs):
'''
::keyword args::
name
product_version
desc
start_date
end_date
suite_list
'''

if kwargs.has_key('product_version'):
product_version_select = Select(self.selenium.find_element(*self._product_version_select_locator))
product_version_select.select_by_visible_text(kwargs['product_version'])

MozTrapEditRunPage.fill_fields(self, **kwargs)


def create_run(self,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trailing whitespace & too many blank lines

name='Test Run',
product_version='Test Product Test Version',
desc='This is a test run',
start_date='2011-01-01',
end_date='2012-12-31',
suite_list=None):
'''
::keyword args::
name
product_version
desc
start_date
end_date
suite_list
'''
dt_string = datetime.utcnow().isoformat()
run = {}
run['name'] = u'%(name)s %(dt_string)s' % {'name': name, 'dt_string': dt_string}
Expand All @@ -41,23 +197,12 @@ def create_run(self, name='Test Run', product_version='Test Product Test Version
run['homepage_locator'] = (self._run_homepage_locator[0], self._run_homepage_locator[1] % {'run_name': run['name']})
run['run_tests_locator'] = self._run_tests_button_locator

name_field = self.selenium.find_element(*self._name_locator)
name_field.send_keys(run['name'])

product_version_select = Select(self.selenium.find_element(*self._product_version_select_locator))
product_version_select.select_by_visible_text(product_version)

self.selenium.find_element(*self._description_locator).send_keys(run['desc'])

self.type_in_element(self._start_date_locator, start_date)
self.selenium.find_element(*self._end_date_locator).send_keys(end_date)

if suite_list:

for suite in suite_list:
suite_input_element = self.selenium.find_element(By.XPATH, "//article[@data-title='%s']//label" % suite)
suite_input_element.click()
self.selenium.find_element(*self._include_selected_suites_locator).click()
self.selenium.find_element(*self._submit_locator).click()
self.fill_fields(
name=run['name'],
product_version=product_version,
desc=run['desc'],
start_date=start_date,
end_date=end_date,
suite_list=suite_list)

return run
47 changes: 30 additions & 17 deletions pages/manage_runs_page.py
Expand Up @@ -6,30 +6,24 @@

from selenium.webdriver.common.by import By

from pages.page import Page
from pages.base_page import MozTrapBasePage
from pages.create_run_page import MozTrapEditRunPage


class MozTrapManageRunsPage(MozTrapBasePage):

_page_title = 'MozTrap'

_delete_run_locator = (By.CSS_SELECTOR, '#manageruns .itemlist .listitem[data-title="%(run_name)s"] .action-delete')
_run_activate_locator = (By.CSS_SELECTOR, '#manageruns .itemlist .listitem[data-title="%(run_name)s"] .status-action.active')
_run_status_locator = (By.CSS_SELECTOR, '#manageruns .itemlist .listitem[data-title="%(run_name)s"] .status-title')
_filter_input_locator = (By.ID, 'text-filter')
_filter_suggestion_locator = (By.CSS_SELECTOR, '#filter .textual .suggest .suggestion[data-type="name"][data-name="%(filter_name)s"]')
_filter_locator = (By.CSS_SELECTOR, '#filterform .filter-group input[data-name="name"][value="%(filter_name)s"]:checked')
_run_item_locator = (By.CSS_SELECTOR, '#manageruns .itemlist .listitem')

def go_to_manage_runs_page(self):
self.selenium.get(self.base_url + '/manage/runs/')
self.is_the_current_page

def delete_run(self, name='Test Run'):
_delete_locator = (self._delete_run_locator[0], self._delete_run_locator[1] % {'run_name': name})

self.selenium.find_element(*_delete_locator).click()
self.wait_for_ajax()

def filter_runs_by_name(self, name):
_filter_suggestion_locator = (self._filter_suggestion_locator[0], self._filter_suggestion_locator[1] % {'filter_name': name})

Expand All @@ -43,11 +37,30 @@ def remove_name_filter(self, name):
self.selenium.find_element(*self._filter_locator).click()
self.wait_for_ajax()

def activate_run(self, name='Test Run'):
_run_activate_locator = (self._run_activate_locator[0], self._run_activate_locator[1] % {'run_name': name})
_run_status_locator = (self._run_status_locator[0], self._run_status_locator[1] % {'run_name': name})

self.selenium.find_element(*_run_status_locator).click()
self.selenium.find_element(*_run_activate_locator).click()

self.wait_for_ajax()
@property
def get_runs(self):
runs = self.selenium.find_elements(*self._run_item_locator)
return [self.Run(self.testsetup, run) for run in runs]

class Run(Page):
_delete_run_locator = (By.CSS_SELECTOR, '.action-delete')
_run_activate_locator = (By.CSS_SELECTOR, '.status-action.active')
_run_status_locator = (By.CSS_SELECTOR, '.status-title')
_edit_run_locator = (By.CSS_SELECTOR, 'a.edit-link')

def __init__(self, testsetup, webelement):
Page.__init__(self, testsetup)
self.webelement = webelement

def delete(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These would be better as click_delete, click_edit etc as that's the action on the element.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after seeing how many times i have to filter the runs by name, get the list of one run, and do something to it, i am thinking that the manage page needs delete_run(name), activate_run(name) and edit_run(name) after all. that being said, i still think Runs should be sub-objects rather than having to wiggle the locators each time.

self.webelement.find_element(*self._delete_run_locator).click()
self.wait_for_ajax()

def edit(self):
self.webelement.find_element(*self._edit_run_locator).click()
return MozTrapEditRunPage(self.testsetup)

def activate(self):
self.webelement.find_element(*self._run_status_locator).click()
self.webelement.find_element(*self._run_activate_locator).click()
self.wait_for_ajax()
5 changes: 3 additions & 2 deletions pages/page.py
Expand Up @@ -62,8 +62,9 @@ def is_element_visible(self, by, value):
return False

def wait_for_ajax(self):
WebDriverWait(self.selenium, self.timeout).until(lambda s: s.execute_script("return $.active == 0"),
"Wait for AJAX timed out after %s seconds" % self.timeout)
WebDriverWait(self.selenium, self.timeout).until(
lambda s: s.execute_script("return jQuery.active == 0"),
"Wait for AJAX timed out after %s seconds" % self.timeout)

def type_in_element(self, locator, text):
"""
Expand Down