def _attach_file_to_resource(self, realm, name, data=None, filename=None, description=None, replace=False, content_type=None): """Attaches a file to a resource. Assumes the resource exists and has already been navigated to.""" if data is None: data = random_page() if description is None: description = random_sentence() if filename is None: filename = random_word() tc.submit('attachfilebutton', 'attachfile') tc.url(self.url + '/attachment/%s/%s/\\?action=new&' 'attachfilebutton=Attach\\+file$' % (realm, name)) fp = StringIO(data) tc.formfile('attachment', 'attachment', filename, content_type=content_type, fp=fp) tc.formvalue('attachment', 'description', description) if replace: tc.formvalue('attachment', 'replace', True) tc.submit() tc.url(self.url + '/attachment/%s/%s/$' % (realm, name)) return filename
def start(self): """Starts the webserver, and waits for it to come up.""" if 'FIGLEAF' in os.environ: exe = os.environ['FIGLEAF'] if ' ' in exe: # e.g. 'coverage run' args = exe.split() else: args = [exe] else: args = [sys.executable] options = ["--port=%s" % self.port, "-s", "--hostname=127.0.0.1", "--basic-auth=trac,%s," % self.htpasswd] if 'TRAC_TEST_TRACD_OPTIONS' in os.environ: options += os.environ['TRAC_TEST_TRACD_OPTIONS'].split() args.append(os.path.join(self.trac_src, 'trac', 'web', 'standalone.py')) server = Popen(args + options + [self.tracdir], stdout=logfile, stderr=logfile, close_fds=close_fds, cwd=self.command_cwd, ) self.pid = server.pid # Verify that the url is ok timeout = 30 while timeout: try: tc.go(self.url) break except ConnectError: time.sleep(1) timeout -= 1 else: raise Exception('Timed out waiting for server to start.') tc.url(self.url)
def go_to_view_tickets(self, href='report'): """Surf to the View Tickets page. By default this will be the Reports page, but 'query' can be specified for the `href` argument to support non-default configurations.""" self.go_to_front() tc.follow('View Tickets') tc.url(self.url + '/' + href.lstrip('/'))
def create_ticket(self, summary=None, info=None): """Create a new (random) ticket in the test environment. Returns the new ticket number. :param summary: may optionally be set to the desired summary :param info: may optionally be set to a dictionary of field value pairs for populating the ticket. ``info['summary']`` overrides summary. `summary` and `description` default to randomly-generated values. """ self.go_to_front() tc.follow(r"\bNew Ticket\b") tc.notfind(internal_error) if summary is None: summary = random_sentence(5) tc.formvalue('propertyform', 'field_summary', summary) tc.formvalue('propertyform', 'field_description', random_page()) if info: for field, value in info.items(): tc.formvalue('propertyform', 'field_%s' % field, value) tc.submit('submit') # we should be looking at the newly created ticket tc.url(self.url + '/ticket/%s' % (self.ticketcount + 1)) tc.notfind(internal_error) # Increment self.ticketcount /after/ we've verified that the ticket # was created so a failure does not trigger spurious later # failures. self.ticketcount += 1 return self.ticketcount
def go_to_wiki(self, name): """Surf to the page for the given wiki page.""" # Used to go based on a quickjump, but if the wiki pagename isn't # camel case, that won't work. wiki_url = self.url + '/wiki/%s' % name tc.go(wiki_url) tc.url(wiki_url)
def attach_file_to_ticket(self, ticketid, data=None, tempfilename=None, description=None, replace=False, content_type=None): """Attaches a file to the given ticket id, with random data if none is provided. Assumes the ticket exists. """ if data is None: data = random_page() if description is None: description = random_sentence() if tempfilename is None: tempfilename = random_word() self.go_to_ticket(ticketid) # set the value to what it already is, so that twill will know we # want this form. tc.formvalue('attachfile', 'action', 'new') tc.submit() tc.url(self.url + "/attachment/ticket/" \ "%s/\\?action=new&attachfilebutton=Attach\\+file" % ticketid) fp = StringIO(data) tc.formfile('attachment', 'attachment', tempfilename, content_type=content_type, fp=fp) tc.formvalue('attachment', 'description', description) if replace: tc.formvalue('attachment', 'replace', True) tc.submit() tc.url(self.url + '/attachment/ticket/%s/$' % ticketid) return tempfilename
def go_to_admin(self, panel_label=None): """Surf to the webadmin page. Continue surfing to a specific admin page if `panel_label` is specified.""" self.go_to_front() tc.follow(r"\bAdmin\b") tc.url(self.url + '/admin') if panel_label is not None: tc.follow(r"\b%s\b" % panel_label)
def go_to_preferences(self, panel_label=None): """Surf to the preferences page. Continue surfing to a specific preferences panel if `panel_label` is specified.""" self.go_to_front() tc.follow(r"\bPreferences\b") tc.url(self.url + '/prefs') if panel_label is not None: tc.follow(r"\b%s\b" % panel_label)
def go_to_ticket(self, ticketid=None): """Surf to the page for the given ticket ID, or to the NewTicket page if `ticketid` is not specified or is `None`. If `ticketid` is specified, it assumes the ticket exists.""" if ticketid is not None: ticket_url = self.url + '/ticket/%s' % ticketid else: ticket_url = self.url + '/newticket' self.go_to_url(ticket_url) tc.url(ticket_url + '$')
def clone_ticket(self, ticketid): """Create a clone of the given ticket id using the clone button.""" ticket_url = self.url + '/ticket/%s' % ticketid self.go_to_url(ticket_url) tc.formvalue('clone', 'clone', 'Clone') tc.submit() # we should be looking at the newly created ticket self.ticketcount += 1 tc.url('%s/ticket/%s' % (self.url, self.ticketcount), regexp=False) return self.ticketcount
def add_comment(self, ticketid, comment=None): """Adds a comment to the given ticket ID, assumes ticket exists.""" self.go_to_ticket(ticketid) if comment is None: comment = random_sentence() tc.formvalue('propertyform', 'comment', comment) tc.submit("submit") # Verify we're where we're supposed to be. tc.url(self.url + '/ticket/%s#comment:.*' % ticketid) return comment
def clone_ticket(self, ticketid): """Create a clone of the given ticket id using the clone button.""" ticket_url = self.url + '/ticket/%s' % ticketid tc.go(ticket_url) tc.url(ticket_url) tc.formvalue('clone', 'clone', 'Clone') tc.submit() # we should be looking at the newly created ticket self.ticketcount += 1 tc.url(self.url + "/ticket/%s" % self.ticketcount) return self.ticketcount
def _ensure_web_access_is_working(self): timeout = 30 while timeout: try: tc.go(self.url) break except (ConnectError, BrowserStateError): time.sleep(1) timeout -= 1 else: raise Exception('Timed out waiting for server to start.') tc.url(self.url)
def add_comment(self, ticketid, comment=None): """Adds a comment to the given ticket ID, assumes ticket exists.""" self.go_to_ticket(ticketid) if comment is None: comment = random_sentence() tc.formvalue('propertyform', 'comment', comment) tc.submit("submit") # Verify we're where we're supposed to be. # The fragment is stripped since Python 2.7.1, see: # http://trac.edgewall.org/ticket/9990#comment:18 tc.url(self.url + '/ticket/%s(?:#comment:.*)?$' % ticketid) return comment
def login(self, username): """Login as the given user""" tc.add_auth("", self.url, username, username) self.go_to_front() tc.find("Login") tc.follow("Login") # We've provided authentication info earlier, so this should # redirect back to the base url. tc.find("logged in as %s" % username) tc.find("Logout") tc.url(self.url) tc.notfind(internal_error)
def login(self, username): """Login as the given user""" username = to_utf8(username) tc.add_auth("", self.url, username, username) self.go_to_front() tc.find("Login") tc.follow(r"\bLogin\b") # We've provided authentication info earlier, so this should # redirect back to the base url. tc.find('logged in as[ \t\n]+<span class="trac-author-user">%s</span>' % username) tc.find("Logout") tc.url(self.url) tc.notfind(internal_error)
def create_milestone(self, name=None, due=None): """Creates the specified milestone, with a random name if none is provided. Returns the name of the milestone. """ if name == None: name = random_unique_camel() milestone_url = self.url + "/admin/ticket/milestones" tc.go(milestone_url) tc.url(milestone_url) tc.formvalue('addmilestone', 'name', name) if due: # TODO: How should we deal with differences in date formats? tc.formvalue('addmilestone', 'duedate', due) tc.submit() tc.notfind(internal_error) tc.notfind('Milestone .* already exists') tc.url(milestone_url) tc.find(name) # Make sure it's on the roadmap. tc.follow('Roadmap') tc.url(self.url + "/roadmap") tc.find('Milestone:.*%s' % name) tc.follow(name) tc.url('%s/milestone/%s' % (self.url, unicode_quote(name))) if not due: tc.find('No date set') return name
def create_milestone(self, name=None, due=None): """Creates the specified milestone, with a random name if none is provided. Returns the name of the milestone. """ if name is None: name = random_unique_camel() milestone_url = self.url + "/admin/ticket/milestones" tc.go(milestone_url) tc.url(milestone_url) tc.formvalue('addmilestone', 'name', name) if due: # TODO: How should we deal with differences in date formats? tc.formvalue('addmilestone', 'duedate', due) tc.submit() tc.notfind(internal_error) tc.notfind('Milestone .* already exists') tc.url(milestone_url) tc.find(name) # Make sure it's on the roadmap. tc.follow(r"\bRoadmap\b") tc.url(self.url + "/roadmap") tc.find('Milestone:.*%s' % name) tc.follow(r"\b%s\b" % name) tc.url('%s/milestone/%s' % (self.url, unicode_quote(name))) if not due: tc.find('No date set') return name
def edit_wiki_page(self, name, content=None): """Edits a wiki page, with random content is none is provided. Returns the content. """ if content is None: content = random_page() self.go_to_wiki(name) tc.formvalue('modifypage', 'action', 'edit') tc.submit() tc.formvalue('edit', 'text', content) tc.submit('save') page_url = self.url + '/wiki/%s' % name tc.url(page_url + '$') return content
def create_enum(self, kind, name=None): """Helper to create the specified enum (used for ``priority``, ``severity``, etc). If no name is given, a unique random word is used. The name is returned. """ if name is None: name = random_unique_camel() enum_url = self.url + "/admin/ticket/" + kind self.go_to_url(enum_url) tc.formvalue('addenum', 'name', name) tc.submit() tc.url(re.escape(enum_url) + '#?$') tc.find(name) tc.notfind(internal_error) return name
def create_version(self, name=None, releasetime=None): """Create a new version. The name defaults to a random camel-cased word if not provided.""" version_admin = self.url + "/admin/ticket/versions" if name == None: name = random_unique_camel() tc.go(version_admin) tc.url(version_admin) tc.formvalue('addversion', 'name', name) if releasetime != None: tc.formvalue('addversion', 'time', releasetime) tc.submit() tc.url(version_admin) tc.find(name) tc.notfind(internal_error)
def create_version(self, name=None, releasetime=None): """Create a new version. The name defaults to a random camel-cased word if not provided.""" version_admin = self.url + "/admin/ticket/versions" if name is None: name = random_unique_camel() tc.go(version_admin) tc.url(version_admin) tc.formvalue('addversion', 'name', name) if releasetime is not None: tc.formvalue('addversion', 'time', releasetime) tc.submit() tc.url(version_admin) tc.find(name) tc.notfind(internal_error)
def create_enum(self, kind, name=None): """Helper to create the specified enum (used for ``priority``, ``severity``, etc). If no name is given, a unique random word is used. The name is returned. """ if name == None: name = random_unique_camel() priority_url = self.url + "/admin/ticket/" + kind tc.go(priority_url) tc.url(priority_url) tc.formvalue('addenum', 'name', name) tc.submit() tc.url(priority_url) tc.find(name) tc.notfind(internal_error) return name
def edit_wiki_page(self, name, content=None, comment=None): """Edits a wiki page, with random content is none is provided. and a random comment if none is provided. Returns the content. """ if content is None: content = random_page() if comment is None: comment = random_sentence() self.go_to_wiki(name) tc.submit(formname='modifypage') tc.formvalue('edit', 'text', content) tc.formvalue('edit', 'comment', comment) tc.submit('save') tc.url('%s/wiki/%s' % (self.url, name), regexp=False) return content
def create_component(self, name=None, user=None): """Creates the specified component, with a random camel-cased name if none is provided. Returns the name.""" if name == None: name = random_unique_camel() component_url = self.url + "/admin/ticket/components" tc.go(component_url) tc.url(component_url) tc.formvalue('addcomponent', 'name', name) if user != None: tc.formvalue('addcomponent', 'owner', user) tc.submit() # Verify the component appears in the component list tc.url(component_url) tc.find(name) tc.notfind(internal_error) # TODO: verify the component shows up in the newticket page return name
def edit_wiki_page(self, name, content=None, comment=None): """Edits a wiki page, with random content is none is provided. and a random comment if none is provided. Returns the content. """ if content is None: content = random_page() if comment is None: comment = random_sentence() self.go_to_wiki(name) tc.formvalue('modifypage', 'action', 'edit') tc.submit() tc.formvalue('edit', 'text', content) tc.formvalue('edit', 'comment', comment) tc.submit('save') page_url = self.url + '/wiki/%s' % name tc.url(page_url+'$') return content
def go_to_report(self, id, args=None): """Surf to the specified report. Assumes the report exists. Report variables will be appended if specified. :param id: id of the report :param args: may optionally specify a dictionary of arguments to be encoded as a query string """ report_url = self.url + "/report/%s" % id if args: arglist = [] for param, value in args.items(): arglist.append('%s=%s' % (param.upper(), unicode_quote(value))) report_url += '?' + '&'.join(arglist) tc.go(report_url) tc.url(report_url.encode('string-escape').replace('?', '\?'))
def create_milestone(self, name=None, due=None): """Creates the specified milestone, with a random name if none is provided. Returns the name of the milestone. """ if name is None: name = random_unique_camel() milestone_url = self.url + "/admin/ticket/milestones" self.go_to_url(milestone_url) tc.formvalue('addmilestone', 'name', name) if due: # TODO: How should we deal with differences in date formats? tc.formvalue('addmilestone', 'duedate', due) tc.submit() tc.notfind(internal_error) tc.notfind('Milestone .* already exists') tc.url(milestone_url, regexp=False) tc.find(name) return name
def attach_file_to_wiki(self, name, data=None): """Attaches a file to the given wiki page, with random content if none is provided. Assumes the wiki page exists. """ if data == None: data = random_page() self.go_to_wiki(name) # set the value to what it already is, so that twill will know we # want this form. tc.formvalue('attachfile', 'action', 'new') tc.submit() tc.url(self.url + "/attachment/wiki/" \ "%s/\\?action=new&attachfilebutton=Attach\\+file" % name) tempfilename = random_word() fp = StringIO(data) tc.formfile('attachment', 'attachment', tempfilename, fp=fp) tc.formvalue('attachment', 'description', random_sentence()) tc.submit() tc.url(self.url + '/attachment/wiki/%s/$' % name)
def login(self, username): """Login as the given user""" tc.add_auth('', self.url + '/login', username, username) self.go_to_front() tc.find("Login") url = self.url.replace('://', '://{0}:{0}@'.format(unicode_quote(username))) url = '%s/login?referer=%s' % (url, unicode_quote(self.url)) tc.go(url) tc.notfind(internal_error) tc.url(self.url, regexp=False) # We've provided authentication info earlier, so this should # redirect back to the base url. tc.find( 'logged in as[ \t\n]+<span class="trac-author-user">%s</span>' % username) tc.find("Logout") tc.url(self.url, regexp=False) tc.notfind(internal_error)
def attach_file_to_wiki(self, name, data=None): """Attaches a file to the given wiki page, with random content if none is provided. Assumes the wiki page exists. """ if data == None: data = random_page() self.go_to_wiki(name) # set the value to what it already is, so that twill will know we # want this form. tc.formvalue('attachfile', 'action', 'new') tc.submit() tc.url(self.url + "/attachment/wiki/" \ "%s/\\?action=new&attachfilebutton=Attach\\+file" % name) tempfilename = random_word() fp = StringIO(data) tc.formfile('attachment', 'attachment', tempfilename, fp=fp) tc.formvalue('attachment', 'description', random_sentence()) tc.submit() tc.url(self.url + '/attachment/wiki/%s/$' % name) return tempfilename
def create_component(self, name=None, owner=None, description=None): """Creates the specified component, with a random camel-cased name if none is provided. Returns the name.""" if name is None: name = random_unique_camel() component_url = self.url + "/admin/ticket/components" tc.go(component_url) tc.url(component_url) tc.formvalue('addcomponent', 'name', name) if owner is not None: tc.formvalue('addcomponent', 'owner', owner) tc.submit() # Verify the component appears in the component list tc.url(component_url) tc.find(name) tc.notfind(internal_error) if description is not None: tc.follow(r"\b%s\b" % name) tc.formvalue('modcomp', 'description', description) tc.submit('save') tc.url(component_url) tc.find("Your changes have been saved.") tc.notfind(internal_error) # TODO: verify the component shows up in the newticket page return name
def create_component(self, name=None, owner=None, description=None): """Creates the specified component, with a random camel-cased name if none is provided. Returns the name.""" if name is None: name = random_unique_camel() component_url = self.url + "/admin/ticket/components" tc.go(component_url) tc.url(component_url) tc.formvalue('addcomponent', 'name', name) if owner is not None: tc.formvalue('addcomponent', 'owner', owner) tc.submit() # Verify the component appears in the component list tc.url(component_url) tc.find(name) tc.notfind(internal_error) if description is not None: tc.follow(r"\b%s\b" % name) tc.formvalue('edit', 'description', description) tc.submit('save') tc.url(component_url) tc.find("Your changes have been saved.") tc.notfind(internal_error) # TODO: verify the component shows up in the newticket page return name
def create_ticket(self, summary=None, info=None): """Create a new (random) ticket in the test environment. Returns the new ticket number. :param summary: may optionally be set to the desired summary :param info: may optionally be set to a dictionary of field value pairs for populating the ticket. ``info['summary']`` overrides summary. `summary` and `description` default to randomly-generated values. """ info = info or {} self.go_to_front() tc.follow(r"\bNew Ticket\b") tc.notfind(internal_error) if summary is None: summary = random_sentence(5) tc.formvalue('propertyform', 'field_summary', summary) tc.formvalue('propertyform', 'field_description', random_page()) if 'owner' in info: tc.formvalue('propertyform', 'action', 'create_and_assign') tc.formvalue('propertyform', 'action_create_and_assign_reassign_owner', info.pop('owner')) for field, value in info.items(): tc.formvalue('propertyform', 'field_%s' % field, value) tc.submit('submit') tc.notfind(internal_error) # we should be looking at the newly created ticket tc.url('%s/ticket/%s#ticket' % (self.url, self.ticketcount + 1), regexp=False) # Increment self.ticketcount /after/ we've verified that the ticket # was created so a failure does not trigger spurious later # failures. self.ticketcount += 1 return self.ticketcount
def start(self): """Starts the webserver, and waits for it to come up.""" if 'FIGLEAF' in os.environ: exe = os.environ['FIGLEAF'] if ' ' in exe: # e.g. 'coverage run' args = exe.split() else: args = [exe] else: args = [sys.executable] options = [ "--port=%s" % self.port, "-s", "--hostname=127.0.0.1", "--basic-auth=trac,%s," % self.htpasswd ] if 'TRAC_TEST_TRACD_OPTIONS' in os.environ: options += os.environ['TRAC_TEST_TRACD_OPTIONS'].split() args.append(os.path.join(self.trac_src, 'trac', 'web', 'standalone.py')) server = Popen( args + options + [self.tracdir], stdout=logfile, stderr=logfile, close_fds=close_fds, cwd=self.command_cwd, ) self.pid = server.pid # Verify that the url is ok timeout = 30 while timeout: try: tc.go(self.url) break except ConnectError: time.sleep(1) timeout -= 1 else: raise Exception('Timed out waiting for server to start.') tc.url(self.url)
def create_ticket(self, summary=None, info=None): """Create a new (random) ticket in the test environment. Returns the new ticket number. :summary: may optionally be set to the desired summary :info: may optionally be set to a dictionary of field value pairs for populating the ticket. ``info['summary']`` overrides summary. `summary` and `description` default to randomly-generated values. """ self.go_to_front() tc.follow('New Ticket') tc.notfind(internal_error) if summary == None: summary = random_sentence(4) tc.formvalue('propertyform', 'field_summary', summary) tc.formvalue('propertyform', 'field_description', random_page()) if info: for field, value in info.items(): tc.formvalue('propertyform', 'field_%s' % field, value) tc.submit('submit') # we should be looking at the newly created ticket tc.url(self.url + '/ticket/%s' % (self.ticketcount + 1)) # Increment self.ticketcount /after/ we've verified that the ticket # was created so a failure does not trigger spurious later # failures. self.ticketcount += 1 # verify the ticket creation event shows up in the timeline self.go_to_timeline() tc.formvalue('prefs', 'ticket', True) tc.submit() tc.find('Ticket.*#%s.*created' % self.ticketcount) return self.ticketcount
def create_wiki_page(self, page, content=None): """Creates the specified wiki page, with random content if none is provided. """ if content == None: content = random_page() page_url = self.url + "/wiki/" + page tc.go(page_url) tc.url(page_url) tc.find("The page %s does not exist." % page) tc.formvalue('modifypage', 'action', 'edit') tc.submit() tc.url(page_url + '\\?action=edit') tc.formvalue('edit', 'text', content) tc.submit('save') tc.url(page_url + '$') # verify the event shows up in the timeline self.go_to_timeline() tc.formvalue('prefs', 'wiki', True) tc.submit() tc.find(page + ".*created")
def create_wiki_page(self, page, content=None): """Creates the specified wiki page, with random content if none is provided. """ if content == None: content = random_page() page_url = self.url + "/wiki/" + page tc.go(page_url) tc.url(page_url) tc.find("The page %s does not exist." % page) tc.formvalue('modifypage', 'action', 'edit') tc.submit() tc.url(page_url + '\\?action=edit') tc.formvalue('edit', 'text', content) tc.submit('save') tc.url(page_url+'$') # verify the event shows up in the timeline self.go_to_timeline() tc.formvalue('prefs', 'wiki', True) tc.submit() tc.find(page + ".*created")
def go_to_front(self): """Go to the Trac front page""" tc.go(self.url) tc.url(self.url) tc.notfind(internal_error)
def go_to_query(self): """Surf to the custom query page.""" self.go_to_front() tc.follow('View Tickets') tc.follow('Custom Query') tc.url(self.url + '/query')
def go_to_url(self, url): tc.go(url) tc.url(url) tc.notfind(internal_error)
def go_to_query(self): """Surf to the custom query page.""" self.go_to_front() tc.follow(r"\bView Tickets\b") tc.follow(r"\bNew Custom Query\b") tc.url(self.url + '/query')
def go_to_milestone(self, name): """Surf to the specified milestone page. Assumes milestone exists.""" self.go_to_roadmap() tc.follow('\\bMilestone: %s\\b' % name) tc.url(self.url + '/milestone/%s' % name)
def go_to_url(self, url): tc.go(url) tc.url(re.escape(url)) tc.notfind(internal_error)
def go_to_roadmap(self): """Surf to the roadmap page.""" self.go_to_front() tc.follow('\\bRoadmap\\b') tc.url(self.url + '/roadmap')
def go_to_timeline(self): """Surf to the timeline page.""" self.go_to_front() tc.follow('Timeline') tc.url(self.url + '/timeline')
def go_to_timeline(self): """Surf to the timeline page.""" self.go_to_front() tc.follow(r"\bTimeline\b") tc.url(self.url + '/timeline')
def go_to_ticket(self, ticketid): """Surf to the page for the given ticket ID. Assumes ticket exists.""" ticket_url = self.url + "/ticket/%s" % ticketid tc.go(ticket_url) tc.url(ticket_url)
def go_to_milestone(self, name): """Surf to the specified milestone page. Assumes milestone exists.""" self.go_to_roadmap() tc.follow(r"\bMilestone: %s\b" % name) tc.url(self.url + '/milestone/%s' % name)
def go_to_roadmap(self): """Surf to the roadmap page.""" self.go_to_front() tc.follow(r"\bRoadmap\b") tc.url(self.url + '/roadmap')