/testing/selenium-core/scripts/ui-map-sample.js
http://datanucleus-appengine.googlecode.com/ · JavaScript · 979 lines · 771 code · 86 blank · 122 comment · 25 complexity · 35566840e217d4aa68436828a373e3a2 MD5 · raw file
- // sample UI element mapping definition. This is for http://alistapart.com/,
- // a particularly well structured site on web design principles.
- // in general, the map should capture structural aspects of the system, instead
- // of "content". In other words, interactive elements / assertible elements
- // that can be counted on to always exist should be defined here. Content -
- // for example text or a link that appears in a blog entry - is always liable
- // to change, and will not be fun to represent in this way. You probably don't
- // want to be testing specific content anyway.
- // create the UI mapping object. THIS IS THE MOST IMPORTANT PART - DON'T FORGET
- // TO DO THIS! In order for it to come into play, a user extension must
- // construct the map in this way.
- var myMap = new UIMap();
- // any values which may appear multiple times can be defined as variables here.
- // For example, here we're enumerating a list of top level topics that will be
- // used as default argument values for several UI elements. Check out how
- // this variable is referenced further down.
- var topics = [
- 'Code',
- 'Content',
- 'Culture',
- 'Design',
- 'Process',
- 'User Science'
- ];
- // map subtopics to their parent topics
- var subtopics = {
- 'Browsers': 'Code'
- , 'CSS': 'Code'
- , 'Flash': 'Code'
- , 'HTML and XHTML': 'Code'
- , 'Scripting': 'Code'
- , 'Server Side': 'Code'
- , 'XML': 'Code'
- , 'Brand Arts': 'Content'
- , 'Community': 'Content'
- , 'Writing': 'Content'
- , 'Industry': 'Culture'
- , 'Politics and Money': 'Culture'
- , 'State of the Web': 'Culture'
- , 'Graphic Design': 'Design'
- , 'User Interface Design': 'Design'
- , 'Typography': 'Design'
- , 'Layout': 'Design'
- , 'Business': 'Process'
- , 'Creativity': 'Process'
- , 'Project Management and Workflow': 'Process'
- , 'Accessibility': 'User Science'
- , 'Information Architecture': 'User Science'
- , 'Usability': 'User Science'
- };
- // define UI elements common for all pages. This regular expression does the
- // trick. '^' is automatically prepended, and '$' is automatically postpended.
- // Please note that because the regular expression is being represented as a
- // string, all backslashes must be escaped with an additional backslash. Also
- // note that the URL being matched will always have any trailing forward slash
- // stripped.
- myMap.addPageset({
- name: 'allPages'
- , description: 'all alistapart.com pages'
- , pathRegexp: '.*'
- });
- myMap.addElement('allPages', {
- name: 'masthead'
- // the description should be short and to the point, usually no longer than
- // a single line
- , description: 'top level image link to site homepage'
- // make sure the function returns the XPath ... it's easy to leave out the
- // "return" statement by accident!
- , locator: "xpath=//*[@id='masthead']/a/img"
- , testcase1: {
- xhtml: '<h1 id="masthead"><a><img expected-result="1" /></a></h1>'
- }
- });
- myMap.addElement('allPages', {
- // be VERY CAREFUL to include commas in the correct place. Missing commas
- // and extra commas can cause lots of headaches when debugging map
- // definition files!!!
- name: 'current_issue'
- , description: 'top level link to issue currently being browsed'
- , locator: "//div[@id='ish']/a"
- , testcase1: {
- xhtml: '<div id="ish"><a expected-result="1"></a></div>'
- }
- });
- myMap.addElement('allPages', {
- name: 'section'
- , description: 'top level link to articles section'
- , args: [
- {
- name: 'section'
- , description: 'the name of the section'
- , defaultValues: [
- 'articles'
- , 'topics'
- , 'about'
- , 'contact'
- , 'contribute'
- , 'feed'
- ]
- }
- ]
- // getXPath has been deprecated by getLocator, but verify backward
- // compatability here
- , getXPath: function(args) {
- return "//li[@id=" + args.section.quoteForXPath() + "]/a";
- }
- , testcase1: {
- args: { section: 'feed' }
- , xhtml: '<ul><li id="feed"><a expected-result="1" /></li></ul>'
- }
- });
- myMap.addElement('allPages', {
- name: 'search_box'
- , description: 'site search input field'
- // xpath has been deprecated by locator, but verify backward compatability
- , xpath: "//input[@id='search']"
- , testcase1: {
- xhtml: '<input id="search" expected-result="1" />'
- }
- });
- myMap.addElement('allPages', {
- name: 'search_discussions'
- , description: 'site search include discussions checkbox'
- , locator: 'incdisc'
- , testcase1: {
- xhtml: '<input id="incdisc" expected-result="1" />'
- }
- });
- myMap.addElement('allPages', {
- name: 'search_submit'
- , description: 'site search submission button'
- , locator: 'submit'
- , testcase1: {
- xhtml: '<input id="submit" expected-result="1" />'
- }
- });
- myMap.addElement('allPages', {
- name: 'topics'
- , description: 'sidebar links to topic categories'
- , args: [
- {
- name: 'topic'
- , description: 'the name of the topic'
- , defaultValues: topics
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='topiclist']/ul/li" +
- "/a[text()=" + args.topic.quoteForXPath() + "]";
- }
- , testcase1: {
- args: { topic: 'foo' }
- , xhtml: '<div id="topiclist"><ul><li>'
- + '<a expected-result="1">foo</a>'
- + '</li></ul></div>'
- }
- });
- myMap.addElement('allPages', {
- name: 'copyright'
- , description: 'footer link to copyright page'
- , getLocator: function(args) { return "//span[@class='copyright']/a"; }
- , testcase1: {
- xhtml: '<span class="copyright"><a expected-result="1" /></span>'
- }
- });
- // define UI elements for the homepage, i.e. "http://alistapart.com/", and
- // magazine issue pages, i.e. "http://alistapart.com/issues/234".
- myMap.addPageset({
- name: 'issuePages'
- , description: 'pages including magazine issues'
- , pathRegexp: '(issues/.+)?'
- });
- myMap.addElement('issuePages', {
- name: 'article'
- , description: 'front or issue page link to article'
- , args: [
- {
- name: 'index'
- , description: 'the index of the article'
- // an array of default values for the argument. A default
- // value is one that is passed to the getXPath() method of
- // the container UIElement object when trying to build an
- // element locator.
- //
- // range() may be used to count easily. Remember though that
- // the ending value does not include the right extreme; for
- // example range(1, 5) counts from 1 to 4 only.
- , defaultValues: range(1, 5)
- }
- ]
- , getLocator: function(args) {
- return "//div[@class='item'][" + args.index + "]/h4/a";
- }
- });
- myMap.addElement('issuePages', {
- name: 'author'
- , description: 'article author link'
- , args: [
- {
- name: 'index'
- , description: 'the index of the author, by article'
- , defaultValues: range(1, 5)
- }
- ]
- , getLocator: function(args) {
- return "//div[@class='item'][" + args.index + "]/h5/a";
- }
- });
- myMap.addElement('issuePages', {
- name: 'store'
- , description: 'alistapart.com store link'
- , locator: "//ul[@id='banners']/li/a[@title='ALA Store']/img"
- });
- myMap.addElement('issuePages', {
- name: 'special_article'
- , description: "editor's choice article link"
- , locator: "//div[@id='choice']/h4/a"
- });
- myMap.addElement('issuePages', {
- name: 'special_author'
- , description: "author link of editor's choice article"
- , locator: "//div[@id='choice']/h5/a"
- });
- // define UI elements for the articles page, i.e.
- // "http://alistapart.com/articles"
- myMap.addPageset({
- name: 'articleListPages'
- , description: 'page with article listings'
- , paths: [ 'articles' ]
- });
- myMap.addElement('articleListPages', {
- name: 'issue'
- , description: 'link to issue'
- , args: [
- {
- name: 'index'
- , description: 'the index of the issue on the page'
- , defaultValues: range(1, 10)
- }
- ]
- , getLocator: function(args) {
- return "//h2[@class='ishinfo'][" + args.index + ']/a';
- }
- , genericLocator: "//h2[@class='ishinfo']/a"
- });
- myMap.addElement('articleListPages', {
- name: 'article'
- , description: 'link to article, by issue and article number'
- , args: [
- {
- name: 'issue_index'
- , description: "the index of the article's issue on the page; "
- + 'typically five per page'
- , defaultValues: range(1, 6)
- }
- , {
- name: 'article_index'
- , description: 'the index of the article within the issue; '
- + 'typically two per issue'
- , defaultValues: range(1, 5)
- }
- ]
- , getLocator: function(args) {
- var xpath = "//h2[@class='ishinfo'][" + (args.issue_index || 1) + ']'
- + "/following-sibling::div[@class='item']"
- + '[' + (args.article_index || 1) + "]/h3[@class='title']/a";
- return xpath;
- }
- , genericLocator: "//h2[@class='ishinfo']"
- + "/following-sibling::div[@class='item']/h3[@class='title']/a"
- });
- myMap.addElement('articleListPages', {
- name: 'author'
- , description: 'article author link, by issue and article'
- , args: [
- {
- name: 'issue_index'
- , description: "the index of the article's issue on the page; \
- typically five per page"
- , defaultValues: range(1, 6)
- }
- , {
- name: 'article_index'
- , description: "the index of the article within the issue; \
- typically two articles per issue"
- , defaultValues: range(1, 3)
- }
- ]
- // this XPath uses the "following-sibling" axis. The div elements for
- // the articles in an issue are not children, but siblings of the h2
- // element identifying the article.
- , getLocator: function(args) {
- var xpath = "//h2[@class='ishinfo'][" + (args.issue_index || 1) + ']'
- + "/following-sibling::div[@class='item']"
- + '[' + (args.article_index || 1) + "]/h4[@class='byline']/a";
- return xpath;
- }
- , genericLocator: "//h2[@class='ishinfo']"
- + "/following-sibling::div[@class='item']/h4[@class='byline']/a"
- });
- myMap.addElement('articleListPages', {
- name: 'next_page'
- , description: 'link to next page of articles (older)'
- , locator: "//a[contains(text(),'Next page')]"
- });
- myMap.addElement('articleListPages', {
- name: 'previous_page'
- , description: 'link to previous page of articles (newer)'
- , locator: "//a[contains(text(),'Previous page')]"
- });
- // define UI elements for specific article pages, i.e.
- // "http://alistapart.com/articles/culturalprobe"
- myMap.addPageset({
- name: 'articlePages'
- , description: 'pages for actual articles'
- , pathRegexp: 'articles/.+'
- });
- myMap.addElement('articlePages', {
- name: 'title'
- , description: 'article title loop-link'
- , locator: "//div[@id='content']/h1[@class='title']/a"
- });
- myMap.addElement('articlePages', {
- name: 'author'
- , description: 'article author link'
- , locator: "//div[@id='content']/h3[@class='byline']/a"
- });
- myMap.addElement('articlePages', {
- name: 'article_topics'
- , description: 'links to topics under which article is published, before \
- article content'
- , args: [
- {
- name: 'topic'
- , description: 'the name of the topic'
- , defaultValues: keys(subtopics)
- }
- ]
- , getLocator: function(args) {
- return "//ul[@id='metastuff']/li/a"
- + "[@title=" + args.topic.quoteForXPath() + "]";
- }
- });
- myMap.addElement('articlePages', {
- name: 'discuss'
- , description: 'link to article discussion area, before article content'
- , locator: "//ul[@id='metastuff']/li[@class='discuss']/p/a"
- });
- myMap.addElement('articlePages', {
- name: 'related_topics'
- , description: 'links to topics under which article is published, after \
- article content'
- , args: [
- {
- name: 'topic'
- , description: 'the name of the topic'
- , defaultValues: keys(subtopics)
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='learnmore']/p/a"
- + "[@title=" + args.topic.quoteForXPath() + "]";
- }
- });
- myMap.addElement('articlePages', {
- name: 'join_discussion'
- , description: 'link to article discussion area, after article content'
- , locator: "//div[@class='discuss']/p/a"
- });
- myMap.addPageset({
- name: 'topicListingPages'
- , description: 'top level listing of topics'
- , paths: [ 'topics' ]
- });
- myMap.addElement('topicListingPages', {
- name: 'topic'
- , description: 'link to topic category'
- , args: [
- {
- name: 'topic'
- , description: 'the name of the topic'
- , defaultValues: topics
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='content']/h2/a"
- + "[text()=" + args.topic.quoteForXPath() + "]";
- }
- });
- myMap.addElement('topicListingPages', {
- name: 'subtopic'
- , description: 'link to subtopic category'
- , args: [
- {
- name: 'subtopic'
- , description: 'the name of the subtopic'
- , defaultValues: keys(subtopics)
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='content']" +
- "/descendant::a[text()=" + args.subtopic.quoteForXPath() + "]";
- }
- });
- // the following few subtopic page UI elements are very similar. Define UI
- // elements for the code page, which is a subpage under topics, i.e.
- // "http://alistapart.com/topics/code/"
- myMap.addPageset({
- name: 'subtopicListingPages'
- , description: 'pages listing subtopics'
- , pathPrefix: 'topics/'
- , paths: [
- 'code'
- , 'content'
- , 'culture'
- , 'design'
- , 'process'
- , 'userscience'
- ]
- });
- myMap.addElement('subtopicListingPages', {
- name: 'subtopic'
- , description: 'link to a subtopic category'
- , args: [
- {
- name: 'subtopic'
- , description: 'the name of the subtopic'
- , defaultValues: keys(subtopics)
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='content']/h2" +
- "/a[text()=" + args.subtopic.quoteForXPath() + "]";
- }
- });
- // subtopic articles page
- myMap.addPageset({
- name: 'subtopicArticleListingPages'
- , description: 'pages listing the articles for a given subtopic'
- , pathRegexp: 'topics/[^/]+/.+'
- });
- myMap.addElement('subtopicArticleListingPages', {
- name: 'article'
- , description: 'link to a subtopic article'
- , args: [
- {
- name: 'index'
- , description: 'the index of the article'
- , defaultValues: range(1, 51) // the range seems unlimited ...
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='content']/div[@class='item']"
- + "[" + args.index + "]/h3/a";
- }
- , testcase1: {
- args: { index: 2 }
- , xhtml: '<div id="content"><div class="item" /><div class="item">'
- + '<h3><a expected-result="1" /></h3></div></div>'
- }
- });
- myMap.addElement('subtopicArticleListingPages', {
- name: 'author'
- , description: "link to a subtopic article author's page"
- , args: [
- {
- name: 'article_index'
- , description: 'the index of the authored article'
- , defaultValues: range(1, 51)
- }
- , {
- name: 'author_index'
- , description: 'the index of the author when there are multiple'
- , defaultValues: range(1, 4)
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='content']/div[@class='item'][" +
- args.article_index + "]/h4/a[" +
- (args.author_index ? args.author_index : '1') + ']';
- }
- });
- myMap.addElement('subtopicArticleListingPages', {
- name: 'issue'
- , description: 'link to issue a subtopic article appears in'
- , args: [
- {
- name: 'index'
- , description: 'the index of the subtopic article'
- , defaultValues: range(1, 51)
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='content']/div[@class='item']"
- + "[" + args.index + "]/h5/a";
- }
- });
- myMap.addPageset({
- name: 'aboutPages'
- , description: 'the website about page'
- , paths: [ 'about' ]
- });
- myMap.addElement('aboutPages', {
- name: 'crew'
- , description: 'link to site crew member bio or personal website'
- , args: [
- {
- name: 'role'
- , description: 'the role of the crew member'
- , defaultValues: [
- 'ALA Crew'
- , 'Support'
- , 'Emeritus'
- ]
- }
- , {
- name: 'role_index'
- , description: 'the index of the member within the role'
- , defaultValues: range(1, 20)
- }
- , {
- name: 'member_index'
- , description: 'the index of the member within the role title'
- , defaultValues: range(1, 5)
- }
- ]
- , getLocator: function(args) {
- // the first role is kind of funky, and requires a conditional to
- // build the XPath correctly. Its header looks like this:
- //
- // <h3>
- // <span class="caps">ALA 4</span>.0 <span class="caps">CREW</span>
- // </h3>
- //
- // This kind of complexity is a little daunting, but you can see
- // how the format can handle it relatively easily and concisely.
- if (args.role == 'ALA Crew') {
- var selector = "descendant::text()='CREW'";
- }
- else {
- var selector = "text()=" + args.role.quoteForXPath();
- }
- var xpath =
- "//div[@id='secondary']/h3[" + selector + ']' +
- "/following-sibling::dl/dt[" + (args.role_index || 1) + ']' +
- '/a[' + (args.member_index || '1') + ']';
- return xpath;
- }
- });
- myMap.addPageset({
- name: 'searchResultsPages'
- , description: 'pages listing search results'
- , paths: [ 'search' ]
- });
- myMap.addElement('searchResultsPages', {
- name: 'result_link'
- , description: 'search result link'
- , args: [
- {
- name: 'index'
- , description: 'the index of the search result'
- , defaultValues: range(1, 11)
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='content']/ul[" + args.index + ']/li/h3/a';
- }
- });
- myMap.addElement('searchResultsPages', {
- name: 'more_results_link'
- , description: 'next or previous results link at top or bottom of page'
- , args: [
- {
- name: 'direction'
- , description: 'next or previous results page'
- // demonstrate a method which acquires default values from the
- // document object. Such default values may contain EITHER commas
- // OR equals signs, but NOT BOTH.
- , getDefaultValues: function(inDocument) {
- var defaultValues = [];
- var divs = inDocument.getElementsByTagName('div');
- for (var i = 0; i < divs.length; ++i) {
- if (divs[i].className == 'pages') {
- break;
- }
- }
- var links = divs[i].getElementsByTagName('a');
- for (i = 0; i < links.length; ++i) {
- defaultValues.push(links[i].innerHTML
- .replace(/^\xab\s*/, "")
- .replace(/\s*\bb$/, "")
- .replace(/\s*\d+$/, ""));
- }
- return defaultValues;
- }
- }
- , {
- name: 'position'
- , description: 'position of the link'
- , defaultValues: ['top', 'bottom']
- }
- ]
- , getLocator: function(args) {
- return "//div[@id='content']/div[@class='pages']["
- + (args.position == 'top' ? '1' : '2') + ']'
- + "/a[contains(text(), "
- + (args.direction ? args.direction.quoteForXPath() : undefined)
- + ")]";
- }
- });
-
-
-
- myMap.addPageset({
- name: 'commentsPages'
- , description: 'pages listing comments made to an article'
- , pathRegexp: 'comments/.+'
- });
- myMap.addElement('commentsPages', {
- name: 'article_link'
- , description: 'link back to the original article'
- , locator: "//div[@id='content']/h1[@class='title']/a"
- });
- myMap.addElement('commentsPages', {
- name: 'comment_link'
- , description: 'same-page link to comment'
- , args: [
- {
- name: 'index'
- , description: 'the index of the comment'
- , defaultValues: range(1, 11)
- }
- ]
- , getLocator: function(args) {
- return "//div[@class='content']/div[contains(@class, 'comment')]" +
- '[' + args.index + ']/h4/a[2]';
- }
- });
- myMap.addElement('commentsPages', {
- name: 'paging_link'
- , description: 'links to more pages of comments'
- , args: [
- {
- name: 'dest'
- , description: 'the destination page'
- , defaultValues: ['next', 'prev'].concat(range(1, 16))
- }
- , {
- name: 'position'
- , description: 'position of the link'
- , defaultValues: ['top', 'bottom']
- }
- ]
- , getLocator: function(args) {
- var dest = args.dest;
- var xpath = "//div[@id='content']/div[@class='pages']" +
- '[' + (args.position == 'top' ? '1' : '2') + ']/p';
- if (dest == 'next' || dest == 'prev') {
- xpath += "/a[contains(text(), " + dest.quoteForXPath() + ")]";
- }
- else {
- xpath += "/a[text()=" + dest.quoteForXPath() + "]";
- }
- return xpath;
- }
- });
- myMap.addPageset({
- name: 'authorPages'
- , description: 'personal pages for each author'
- , pathRegexp: 'authors/[a-z]/.+'
- });
- myMap.addElement('authorPages', {
- name: 'article'
- , description: "link to article written by this author.\n"
- + 'This description has a line break.'
- , args: [
- {
- name: 'index'
- , description: 'index of the article on the page'
- , defaultValues: range(1, 11)
- }
- ]
- , getLocator: function(args) {
- var index = args.index;
- // try out the CSS locator!
- //return "//h4[@class='title'][" + index + "]/a";
- return 'css=h4.title:nth-child(' + index + ') > a';
- }
- , testcase1: {
- args: { index: '2' }
- , xhtml: '<h4 class="title" /><h4 class="title">'
- + '<a expected-result="1" /></h4>'
- }
- });
- // test the offset locator. Something like the following can be recorded:
- // ui=qaPages::content()//a[contains(text(),'May I quote from your articles?')]
- myMap.addPageset({
- name: 'qaPages'
- , description: 'question and answer pages'
- , pathRegexp: 'qa'
- });
- myMap.addElement('qaPages', {
- name: 'content'
- , description: 'the content pane containing the q&a entries'
- , locator: "//div[@id='content' and "
- + "child::h1[text()='Questions and Answers']]"
- , getOffsetLocator: UIElement.defaultOffsetLocatorStrategy
- });
- myMap.addElement('qaPages', {
- name: 'last_updated'
- , description: 'displays the last update date'
- // demonstrate calling getLocator() for another UI element within a
- // getLocator(). The former must have already been added to the map. And
- // obviously, you can't randomly combine different locator types!
- , locator: myMap.getUIElement('qaPages', 'content').getLocator() + '/p/em'
- });
- //******************************************************************************
- var myRollupManager = new RollupManager();
- // though the description element is required, its content is free form. You
- // might want to create a documentation policy as given below, where the pre-
- // and post-conditions of the rollup are spelled out.
- //
- // To take advantage of a "heredoc" like syntax for longer descriptions,
- // add a backslash to the end of the current line and continue the string on
- // the next line.
- myRollupManager.addRollupRule({
- name: 'navigate_to_subtopic_article_listing'
- , description: 'drill down to the listing of articles for a given subtopic \
- from the section menu, then the topic itself.'
- , pre: 'current page contains the section menu (most pages should)'
- , post: 'navigated to the page listing all articles for a given subtopic'
- , args: [
- {
- name: 'subtopic'
- , description: 'the subtopic whose article listing to navigate to'
- , exampleValues: keys(subtopics)
- }
- ]
- , commandMatchers: [
- {
- command: 'clickAndWait'
- , target: 'ui=allPages::section\\(section=topics\\)'
- // must escape parentheses in the the above target, since the
- // string is being used as a regular expression. Again, backslashes
- // in strings must be escaped too.
- }
- , {
- command: 'clickAndWait'
- , target: 'ui=topicListingPages::topic\\(.+'
- }
- , {
- command: 'clickAndWait'
- , target: 'ui=subtopicListingPages::subtopic\\(.+'
- , updateArgs: function(command, args) {
- // don't bother stripping the "ui=" prefix from the locator
- // here; we're just using UISpecifier to parse the args out
- var uiSpecifier = new UISpecifier(command.target);
- args.subtopic = uiSpecifier.args.subtopic;
- return args;
- }
- }
- ]
- , getExpandedCommands: function(args) {
- var commands = [];
- var topic = subtopics[args.subtopic];
- var subtopic = args.subtopic;
- commands.push({
- command: 'clickAndWait'
- , target: 'ui=allPages::section(section=topics)'
- });
- commands.push({
- command: 'clickAndWait'
- , target: 'ui=topicListingPages::topic(topic=' + topic + ')'
- });
- commands.push({
- command: 'clickAndWait'
- , target: 'ui=subtopicListingPages::subtopic(subtopic=' + subtopic
- + ')'
- });
- commands.push({
- command: 'verifyLocation'
- , target: 'regexp:.+/topics/.+/.+'
- });
- return commands;
- }
- });
- myRollupManager.addRollupRule({
- name: 'replace_click_with_clickAndWait'
- , description: 'replaces commands where a click was detected with \
- clickAndWait instead'
- , alternateCommand: 'clickAndWait'
- , commandMatchers: [
- {
- command: 'click'
- , target: 'ui=subtopicArticleListingPages::article\\(.+'
- }
- ]
- , expandedCommands: []
- });
- myRollupManager.addRollupRule({
- name: 'navigate_to_subtopic_article'
- , description: 'navigate to an article listed under a subtopic.'
- , pre: 'current page contains the section menu (most pages should)'
- , post: 'navigated to an article page'
- , args: [
- {
- name: 'subtopic'
- , description: 'the subtopic whose article listing to navigate to'
- , exampleValues: keys(subtopics)
- }
- , {
- name: 'index'
- , description: 'the index of the article in the listing'
- , exampleValues: range(1, 11)
- }
- ]
- , commandMatchers: [
- {
- command: 'rollup'
- , target: 'navigate_to_subtopic_article_listing'
- , value: 'subtopic\\s*=.+'
- , updateArgs: function(command, args) {
- var args1 = parse_kwargs(command.value);
- args.subtopic = args1.subtopic;
- return args;
- }
- }
- , {
- command: 'clickAndWait'
- , target: 'ui=subtopicArticleListingPages::article\\(.+'
- , updateArgs: function(command, args) {
- var uiSpecifier = new UISpecifier(command.target);
- args.index = uiSpecifier.args.index;
- return args;
- }
- }
- ]
- /*
- // this is pretty much equivalent to the commandMatchers immediately above.
- // Seems more verbose and less expressive, doesn't it? But sometimes you
- // might prefer the flexibility of a function.
- , getRollup: function(commands) {
- if (commands.length >= 2) {
- command1 = commands[0];
- command2 = commands[1];
- var args1 = parse_kwargs(command1.value);
- try {
- var uiSpecifier = new UISpecifier(command2.target
- .replace(/^ui=/, ''));
- }
- catch (e) {
- return false;
- }
- if (command1.command == 'rollup' &&
- command1.target == 'navigate_to_subtopic_article_listing' &&
- args1.subtopic &&
- command2.command == 'clickAndWait' &&
- uiSpecifier.pagesetName == 'subtopicArticleListingPages' &&
- uiSpecifier.elementName == 'article') {
- var args = {
- subtopic: args1.subtopic
- , index: uiSpecifier.args.index
- };
- return {
- command: 'rollup'
- , target: this.name
- , value: to_kwargs(args)
- , replacementIndexes: [ 0, 1 ]
- };
- }
- }
- return false;
- }
- */
- , getExpandedCommands: function(args) {
- var commands = [];
- commands.push({
- command: 'rollup'
- , target: 'navigate_to_subtopic_article_listing'
- , value: to_kwargs({ subtopic: args.subtopic })
- });
- var uiSpecifier = new UISpecifier(
- 'subtopicArticleListingPages'
- , 'article'
- , { index: args.index });
- commands.push({
- command: 'clickAndWait'
- , target: 'ui=' + uiSpecifier.toString()
- });
- commands.push({
- command: 'verifyLocation'
- , target: 'regexp:.+/articles/.+'
- });
- return commands;
- }
- });