Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

address #73 #80

Closed
wants to merge 4 commits into from

9 participants

@klrmn
Collaborator

Please tell me what you think of this pull, before i get in any deeper.

  • fix wait_for_ajax
  • MozTrapEditRunPage
  • treat runs in manage runs list as sub-objects
  • treat suits in create/edit run as sub-objects
  • allow for both addition and removal of suites in runs

TODO:
figure out how to drag-n-drop

UPDATE:
pull is now fully ready. note that the test requires native.

@klrmn klrmn partially address #73
* fix wait_for_ajax
* MozTrapEditRunPage
* treat runs in manage runs list as sub-objects
* treat suits in create/edit run as sub-objects
* allow for both addition and removal of suites in runs

TODO:
figure out how to drag-n-drop
7a01058
pages/create_run_page.py
((26 lines not shown))
_submit_locator = (By.CSS_SELECTOR, '#run-add-form .form-actions > button')
+
+ # Note on __init__: it fails with 'ReferenceError: jQuery is not defined'
@zacc
zacc added a note

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@zacc zacc commented on the diff
pages/manage_runs_page.py
((12 lines not shown))
+ @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):
@zacc
zacc added a note

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

@klrmn Collaborator
klrmn added a note

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
pages/create_run_page.py
((42 lines not shown))
+ 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'):
+ 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']:
@klrmn Collaborator
klrmn added a note

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.

@zacc
zacc added a note

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

@zacc
zacc added a note

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

@klrmn Collaborator
klrmn added a note
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
pages/create_run_page.py
((45 lines not shown))
+ (key, "\n".join(self._known_kwargs)))
+
+ if kwargs.has_key('name'):
+ 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']:
+ # remove unwanted
+ for suite in self.included_suites:
+ print 'included has %s' % suite.name
@teodosia Collaborator

We should delete this print and the ones below.

@klrmn Collaborator
klrmn added a note

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@AlinT AlinT commented on the diff
pages/create_run_page.py
((5 lines not shown))
from pages.base_page import MozTrapBasePage
-
-class MozTrapCreateRunPage(MozTrapBasePage):
-
- _page_title = 'MozTrap'
-
+class MozTrapEditRunPage(MozTrapBasePage):
@AlinT
AlinT added a note

expected 2 blank lines, found 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
pages/create_run_page.py
((26 lines not shown))
_submit_locator = (By.CSS_SELECTOR, '#run-add-form .form-actions > button')
+
+ def __init__(self, testsetup):
+ MozTrapBasePage.__init__(self, testsetup)
+ self.wait_for_ajax()
+
+ def fill_fields(self, **kwargs):
+ '''
+ ::keyword args::
+ name
+ desc
+ start_date
+ end_date
+ suite_list
+ '''
+
@AlinT
AlinT added a note

blank line contains whitespace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
pages/create_run_page.py
((32 lines not shown))
+ def fill_fields(self, **kwargs):
+ '''
+ ::keyword args::
+ name
+ desc
+ start_date
+ end_date
+ suite_list
+ '''
+
+ 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'):
@AlinT
AlinT added a note

.has_key() is deprecated, use 'in'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
pages/create_run_page.py
((163 lines not shown))
+ 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,
@AlinT
AlinT added a note

trailing whitespace & too many blank lines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
pages/base_test.py
@@ -82,8 +82,9 @@ 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()
+
@AlinT
AlinT added a note

blank line contains whitespace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/test_manage_runs_page.py
((14 lines not shown))
self.delete_version(mozwebqa_logged_in, version=run['version'], delete_product=True)
+
+ @pytest.mark.moztrap(2929)
+ def test_edit_existing_run_that_includes_suites(self, mozwebqa_logged_in):
+ # setup
+ product = self.create_product(mozwebqa_logged_in)
+ version = self.create_version(mozwebqa_logged_in, product=product)
+ suite1 = self.create_suite(mozwebqa_logged_in, product=product)
+ suite2 = self.create_suite(mozwebqa_logged_in, product=product)
@AlinT
AlinT added a note

trailing whitespace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/test_manage_runs_page.py
@@ -9,6 +9,7 @@
from pages.base_test import BaseTest
from pages.manage_runs_page import MozTrapManageRunsPage
+from pages.manage_suites_page import MozTrapManageSuitesPage
@AlinT
AlinT added a note

Unused import

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@klrmn klrmn review feedback and drag-and-drop. fixes #73
* pep8 fixes
* don't remove presumed unwanted suites in fill_fields, provide
additional methods for adding and removing suites to/from runs.
* implement drag-and-drop (note: this uses native)
fef251a
@teodosia teodosia commented on the diff
pages/create_run_page.py
((87 lines not shown))
+ @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]
+ 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
@teodosia Collaborator

We should remove this print.

@klrmn Collaborator
klrmn added a note

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@AlinT

The test passes and the code looks good but it takes a huge amount of time to run(~520 seconds).

tests/test_manage_runs_page.py
((49 lines not shown))
+ # add suite2 to the run
+ edit_run_pg.add_suite(suite2['name'])
+ # re-order suites to run suite2 first
+ edit_run_pg.drag_and_drop_suite(suite2['name'], suite1['name'])
+ # save
+ manage_runs_pg = edit_run_pg.click_save()
+ # click edit
+ manage_runs_pg.filter_runs_by_name(name=run['name'])
+ runs = manage_runs_pg.get_runs
+ edit_run_pg = runs[0].edit()
+ expected = [suite2['name'], suite1['name']]
+ actual = edit_run_pg.included_suite_names
+ assert actual == expected
+
+ # teardown
+ self.delete_run(mozwebqa_logged_in, run)
@m8ttyB Owner
m8ttyB added a note

If an above assertion fails the tear down won't run. Is this a problem?

@bobsilverberg Owner

@m8ttyB, @klrmn: why not use py.test's teardown instead of a simulated teardown?

@klrmn Collaborator
klrmn added a note

because i don't know how to do setup() and teardown() methods that apply to just one out of many of the test cases in the class, and each of the test cases has a slightly different fixture set. if someone can suggest how, i'm happy to learn it. the same thing had been bothering me.

@bobsilverberg Owner

I don't believe that you can have setup or teardown run for a single test case (or selected test cases) in a class, but I would think that just moving the test into its own file would be an option.

You could look into using a global variable to control the behaviour of teardown, but that seems overly complicated to me.

@klrmn Collaborator
klrmn added a note
@m8ttyB Owner
m8ttyB added a note

@klrmn good point -- I didn't realize the other tests for this product followed the same convention. I withdraw my criticism and will put it into a git issue :-)

@bebef1987 Collaborator

don't know if this is the case but can't we use something like http://pytest.org/2.2.4/funcargs.html#_pytest.python.FuncargRequest.cached_setup
here?
I never used it but from the docs it looks like it would fit in our tests

@zacc
zacc added a note

I have tried to do setup and teardown in py.test as we have a similar problem in flightdeck-tests. It's the one thing I really miss from junit/testng.

I managed to get a setup/teardown but couldn't get the mozwebqa/selenium session into it.

At the moment we just have to deal with it I think

@klrmn Collaborator
klrmn added a note

yeah...if we have the api, then we don't need mozwebqa in the setup/teardown, but it is otherwise problematic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@klrmn
Collaborator

@AlinT , yes, it does take forever to run, because of the setup and teardown being done via selenium rather than via an API that doesn't exist yet. I requested the API tho =).

@bebef1987
Collaborator

http://pastebin.mozilla.org/1894532

this takes too long to run...
and we really need a API for this site...
Can we talk with the devs to maybe check and see is there is something like this in place...

@bebef1987
Collaborator

@jgerigmeyer @rbillings @camd @carljm do we have a API for adding tests suites runs ?

@bebef1987 bebef1987 referenced this pull request
Closed

Issue 74 #82

tests/test_manage_runs_page.py
((46 lines not shown))
+ assert suite1['name'] in edit_run_pg.included_suite_names
+ assert suite2['name'] in edit_run_pg.available_suite_names
+
+ # add suite2 to the run
+ edit_run_pg.add_suite(suite2['name'])
+ # re-order suites to run suite2 first
+ edit_run_pg.drag_and_drop_suite(suite2['name'], suite1['name'])
+ # save
+ manage_runs_pg = edit_run_pg.click_save()
+ # click edit
+ manage_runs_pg.filter_runs_by_name(name=run['name'])
+ runs = manage_runs_pg.get_runs
+ edit_run_pg = runs[0].edit()
+ expected = [suite2['name'], suite1['name']]
+ actual = edit_run_pg.included_suite_names
+ assert actual == expected
@zacc
zacc added a note

Any reason not using unittest-zero in this test?

@klrmn Collaborator
klrmn added a note

because i got out of the habit =)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@camd
@AlinT

The code looks good and the tests pass on Mac+FF14 but fail on Win7+FF14
I suspect the locators need to be strengthen, but any idea is welcomed!

@klrmn
Collaborator
@AlinT

@klrmn Ran this again with Webdriver 2.26.0 on Win7 + FF16&15, and Webdriver 2.25.0 on Win7 + FF 14.
On all cases the test failed with the same log http://pastebin.mozilla.org/1902059.
Hope this helps you, and I'd really appreciate if other people would run this on Windows platforms and post a comment with their findings/conclusions so that we can rule out the problem of the environment.
Thanks in advance!

@klrmn
Collaborator

@AlinT

This particular failure means that drag-and-drop isn't working. So far as i know, this is the first mozwebqa repo to try to use drag-and-drop.

@AutomatedTester, after you suggested i had a version mismatch, i got the drag and drop to work on:
Mac+FF13+Selenium 2.25,
Mac+FF15+Selenium 2.25,
XP+FF15+Selenium 2.25
(probably some other combos)

but @AlinT is seeing failures listed above.

any ideas? i would suggest sending it to sauce so we can watch a recording, but Win7 doesn't appear to be an option there. (I think the selenium tests are being run on sauce, so this might mean it's not being tested before selenium release?)

-Leah

@bebef1987
Collaborator

(moztrap)florinstrugariu@P4298:~/work/moztrap-tests-bebe$ py.test --baseurl=http://moztrap-dev.allizom.org/ --browserver=16 --browsername=firefox --platform=mac --credentials=/home/florinstrugariu/work/webqa-credentials/moztrap.yaml --destructive --host=webqa.local tests/test_manage_runs_page.py
================================================================================================ test session starts ================================================================================================
platform linux2 -- Python 2.7.3 -- pytest-2.2.4
collected 2 items

tests/test_manage_runs_page.py X.

====================================================================================== 1 passed, 1 xpassed in 1073.96 seconds =======================================================================================

@bebef1987
Collaborator

this looks OK but... this is not a test that could be merged...
2 test in 1073 (17 min) jenkins will throw it out without looking at it...

In my opinion without the API to create test data fast this task is blocked

@camd

Hey there. We are working to prioritize MozTrap work and evaluating whether we can get these APIs added in now or need to wait till after the new year due to higher priorities. We'll keep you posted. Sorry for your pain, but thanks for your hard work!

@bebef1987
Collaborator

Thanks for the info @camd let's hope we get a cool api for Christmas Ninja cat was a really good cat :smile:

@m8ttyB should we merge this?

@m8ttyB
Owner

I need some time to understand what's going on, what's being tested, and if there is an alternate path we can take to setting up and running the test -- let's take the discussion to the mailing list. I'm hopeful we can find a sneaky way to get this test automated without an API.

As @camd noted we're really not likely to get an API for some time unless of course someone from the community wants to reach out to @camd for mentorship and give it shot themselves (which would be quite cool).

@m8ttyB
Owner

Just for reference here is the testcase that is being automated -- correct me if I'm wrong :)

https://moztrap.mozilla.org/manage/cases/?filter-id=2929#caseversion-id-10687

And because I love tiny #squee goats

@sashakruglov
Collaborator

It is possible to reduce time for test cases creation by using bulk create.

@m8ttyB
Owner

Hi @sashakruglov I don't think I understand what your question is? May I suggest you start a thread on our mailing list so the team can respond?

mozwebqa@mozilla.org

@m8ttyB
Owner

@klrmn should we close this pull request and then reissue it once an API is in place? This pull has been open for quite some time.

@sashakruglov
Collaborator

@m8ttyB I was talking about this part of test https://github.com/mozilla/moztrap-tests/pull/80/files#L4R41 and was trying to say that it's possible to create 4 test cases at once by bulk create option, so it will reduce execution time of test for maybe a minute or a little less than a minute.

Another option is to use direct POST requests to create test items.
I tried and it works, there are some difficulties though:

  1. it requires sessionid and maybe anoncsrf token (so, it will require log in action in browser to obtain session)
  2. it requires another csrf token (which can be obtained by driver.find_element(By.NAME, 'csrfmiddlewaretoken').get_attribute('value')
  3. and most difficult one is that some parameters that are sent to server are IDs of items (like ID of profile).

We can try this approach, I think we can win some time by it. What do you think?

Also, I might look into creating requested API, but I'm not so sure that I'm capable enough to implement it, but I would like to give it a shot.

@bobsilverberg

Can we either resolve the merge conflicts on this or close it?

@bobsilverberg

Closing due to age. Yes, when it comes to pull requests I am ageist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 24, 2012
  1. @klrmn

    partially address #73

    klrmn authored
    * fix wait_for_ajax
    * MozTrapEditRunPage
    * treat runs in manage runs list as sub-objects
    * treat suits in create/edit run as sub-objects
    * allow for both addition and removal of suites in runs
    
    TODO:
    figure out how to drag-n-drop
  2. @klrmn
Commits on Oct 25, 2012
  1. @klrmn

    review feedback and drag-and-drop. fixes #73

    klrmn authored
    * pep8 fixes
    * don't remove presumed unwanted suites in fill_fields, provide
    additional methods for adding and removing suites to/from runs.
    * implement drag-and-drop (note: this uses native)
Commits on Oct 31, 2012
  1. @klrmn
This page is out of date. Refresh to see the latest.
View
6 pages/base_test.py
@@ -82,7 +82,8 @@ 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()
return run
@@ -91,7 +92,8 @@ def delete_run(self, mozwebqa, run, delete_version=False, delete_product=False):
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)
View
225 pages/create_run_page.py
@@ -9,30 +9,212 @@
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):
@AlinT
AlinT added a note

expected 2 blank lines, found 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ _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')
- _submit_locator = (By.CSS_SELECTOR, '#run-add-form .form-actions > button')
+ _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, '.form-actions > button')
+
+ def __init__(self, testsetup):
+ MozTrapBasePage.__init__(self, testsetup)
+ self.wait_for_ajax()
+
+ def fill_fields(self, **kwargs):
+ '''
+ ::keyword args::
+ name
+ desc
+ start_date
+ end_date
+ suite_list
+ '''
+
+ 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 'name' in kwargs.keys():
+ name_field = self.selenium.find_element(*self._name_locator)
+ name_field.send_keys(kwargs['name'])
+ if 'desc' in kwargs.keys():
+ self.selenium.find_element(*self._description_locator).send_keys(kwargs['desc'])
+ if 'start_date' in kwargs.keys():
+ self.type_in_element(self._start_date_locator, kwargs['start_date'])
+ if 'end_date' in kwargs.keys():
+ self.selenium.find_element(*self._end_date_locator).send_keys(kwargs['end_date'])
+
+ if 'suite_list' in kwargs.keys() and kwargs['suite_list']:
+ for suite in kwargs['suite_list']:
+ self.add_suite(suite)
+
+ return self.click_save()
+
+ def click_save(self):
+ self.selenium.find_element(*self._submit_locator).click()
+ from pages.manage_runs_page import MozTrapManageRunsPage
+ return MozTrapManageRunsPage(self.testsetup)
+
+ def add_suite(self, suite_name):
+ suites = self.available_suites
+ for suite in suites:
+ if suite.name == suite_name:
+ suite.select()
+ self.add_selected_suites()
+ return
+ raise Exception("suite '%s' was not found for addition" % suite_name)
+
+ def remove_suite(self, suite_name):
+ suites = self.included_suites
+ for suite in suites:
+ if suite.name == suite_name:
+ suite.select()
+ self.remove_selected_suites()
+ return
+ raise Exception("suite '%s' was not found for removal" % suite_name)
+
+ @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]
+ 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
@teodosia Collaborator

We should remove this print.

@klrmn Collaborator
klrmn added a note

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ 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()
+
+ def drag_and_drop_suite(self, suite_name_to_move, destination_suite_name):
+ '''Drag and Drop one suite in the included list to the location of
+ another suite in the same list.
+
+ ::Args::
+ suite_name_to_move
+ destination_suite_name
+
+ ::Note::
+ This method does not work with some combinations of versions of
+ Firefox / Selenium. It is known to work with FF12 + Selenium 2.18
+ and FF15 + Selenium 2.25.
+ '''
+ from selenium.webdriver.common.action_chains import ActionChains
+ suites = self.included_suites
+ for suite in suites:
+ if suite.name == suite_name_to_move:
+ suite_to_move = suite
+ if suite.name == destination_suite_name:
+ destination = suite
+ ActionChains(self.selenium).drag_and_drop(
+ suite_to_move.webelement, destination.webelement).perform()
+
+ 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()
+
+
+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 'product_version' in kwargs.keys():
+ 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,
+ 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}
@@ -41,23 +223,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
View
47 pages/manage_runs_page.py
@@ -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})
@@ -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):
@zacc
zacc added a note

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

@klrmn Collaborator
klrmn added a note

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ 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()
View
5 pages/page.py
@@ -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 (typeof jQuery !== "undefined" && jQuery.active == 0)'),
+ "Wait for AJAX timed out after %s seconds" % self.timeout)
def type_in_element(self, locator, text):
"""
View
62 tests/test_manage_runs_page.py
@@ -21,10 +21,66 @@ def test_that_user_can_create_and_delete_run(self, mozwebqa_logged_in):
manage_runs_pg.filter_runs_by_name(name=run['name'])
- Assert.true(manage_runs_pg.is_element_present(*run['manage_locator']))
+ runs = manage_runs_pg.get_runs
+ Assert.equal(len(runs), 1)
- manage_runs_pg.delete_run(name=run['name'])
+ runs[0].delete()
- Assert.false(manage_runs_pg.is_element_present(*run['manage_locator']))
+ runs = manage_runs_pg.get_runs
+ Assert.equal(len(runs), 0)
self.delete_version(mozwebqa_logged_in, version=run['version'], delete_product=True)
+
+ @pytest.mark.moztrap(2929)
+ @pytest.mark.native
+ def test_edit_existing_run_that_includes_suites(self, mozwebqa_logged_in):
+ # setup
+ product = self.create_product(mozwebqa_logged_in)
+ suite1 = self.create_suite(mozwebqa_logged_in, product=product)
+ suite2 = self.create_suite(mozwebqa_logged_in, product=product)
+ case1 = self.create_case(mozwebqa_logged_in,
+ product=product, version=product['version'], suite_name=suite1['name'])
+ case2 = self.create_case(mozwebqa_logged_in,
+ product=product, version=product['version'], suite_name=suite1['name'])
+ case3 = self.create_case(mozwebqa_logged_in,
+ product=product, version=product['version'], suite_name=suite2['name'])
+ case4 = self.create_case(mozwebqa_logged_in,
+ product=product, version=product['version'], suite_name=suite2['name'])
+ run = self.create_run(mozwebqa_logged_in,
+ product=product, version=product['version'],
+ suite_name_list=[suite1['name']])
+
+ # go to manage runs page
+ manage_runs_pg = MozTrapManageRunsPage(mozwebqa_logged_in)
+ # find the run
+ manage_runs_pg.filter_runs_by_name(name=run['name'])
+ runs = manage_runs_pg.get_runs
+ # click edit
+ edit_run_pg = runs[0].edit()
+
+ # not yet deployed to stage
+ # assert edit_run_pg.product_version == run['product_version']
+ Assert.contains(suite1['name'], edit_run_pg.included_suite_names)
+ Assert.contains(suite2['name'], edit_run_pg.available_suite_names)
+
+ # add suite2 to the run
+ edit_run_pg.add_suite(suite2['name'])
+ # re-order suites to run suite2 first
+ edit_run_pg.drag_and_drop_suite(suite2['name'], suite1['name'])
+ # save
+ manage_runs_pg = edit_run_pg.click_save()
+ # click edit
+ manage_runs_pg.filter_runs_by_name(name=run['name'])
+ runs = manage_runs_pg.get_runs
+ edit_run_pg = runs[0].edit()
+ expected = [suite2['name'], suite1['name']]
+ actual = edit_run_pg.included_suite_names
+ Assert.equal(actual, expected)
+
+ # teardown
+ self.delete_run(mozwebqa_logged_in, run)
+ for case in [case1, case2, case3, case4]:
+ self.delete_case(mozwebqa_logged_in, case)
+ for suite in [suite1, suite2]:
+ self.delete_suite(mozwebqa_logged_in, suite)
+ self.delete_product(mozwebqa_logged_in, product)
Something went wrong with that request. Please try again.