1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.test.ui.po

File BasePage.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

10
106
48
1
606
318
53
0.5
2.21
48
1.1

Classes

Class Line # Actions
BasePage 46 106 0% 53 43
0.737804973.8%
 

Contributing tests

This file is covered by 197 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.test.ui.po;
21   
22    import java.util.ArrayList;
23    import java.util.List;
24    import java.util.Locale;
25    import java.util.regex.Matcher;
26    import java.util.regex.Pattern;
27   
28    import org.apache.commons.lang3.LocaleUtils;
29    import org.openqa.selenium.By;
30    import org.openqa.selenium.JavascriptExecutor;
31    import org.openqa.selenium.WebElement;
32    import org.openqa.selenium.support.FindBy;
33    import org.openqa.selenium.support.FindBys;
34    import org.xwiki.test.ui.po.editor.ClassEditPage;
35    import org.xwiki.test.ui.po.editor.ObjectEditPage;
36    import org.xwiki.test.ui.po.editor.RightsEditPage;
37    import org.xwiki.test.ui.po.editor.WYSIWYGEditPage;
38    import org.xwiki.test.ui.po.editor.WikiEditPage;
39   
40    /**
41    * Represents the common actions possible on all Pages.
42    *
43    * @version $Id: aa5aaf77c0b3f02c5cb0e56f2e4d27db72adf759 $
44    * @since 3.2M3
45    */
 
46    public class BasePage extends BaseElement
47    {
48    /**
49    * Used for sending keyboard shortcuts to.
50    */
51    @FindBy(id = "xwikimaincontainer")
52    private WebElement mainContainerDiv;
53   
54    /**
55    * The top floating content menu bar.
56    */
57    @FindBy(id = "contentmenu")
58    private WebElement contentMenuBar;
59   
60    @FindBy(xpath = "//div[@id='tmCreate']/a[contains(@role, 'button')]")
61    private WebElement tmCreate;
62   
63    @FindBy(xpath = "//div[@id='tmMoreActions']/a[contains(@role, 'button')]")
64    private WebElement moreActionsMenu;
65   
66    @FindBy(id = "tmDrawerActivator")
67    private WebElement drawerActivator;
68   
69    @FindBy(xpath = "//input[@id='tmWatchDocument']/../span[contains(@class, 'bootstrap-switch-label')]")
70    private WebElement watchDocumentLink;
71   
72    @FindBy(id = "tmPage")
73    private WebElement pageMenu;
74   
75    @FindBys({ @FindBy(id = "tmRegister"), @FindBy(tagName = "a") })
76    private WebElement registerLink;
77   
78    @FindBy(xpath = "//a[@id='tmLogin']")
79    private WebElement loginLink;
80   
81    @FindBy(xpath = "//a[@id='tmUser']")
82    private WebElement userLink;
83   
84    @FindBy(xpath = "//li[contains(@class, 'navbar-avatar')]//img[contains(@class, 'avatar')]")
85    private WebElement userAvatarImage;
86   
87    @FindBy(id = "document-title")
88    private WebElement documentTitle;
89   
90    @FindBy(xpath = "//input[@id='tmWatchSpace']/../span[contains(@class, 'bootstrap-switch-label')]")
91    private WebElement watchSpaceLink;
92   
93    @FindBy(xpath = "//input[@id='tmWatchWiki']/../span[contains(@class, 'bootstrap-switch-label')]")
94    private WebElement watchWikiLink;
95   
96    @FindBy(css = "#tmMoreActions a[title='Children']")
97    private WebElement childrenLink;
98   
99    @FindBy(id = "tmNotifications")
100    private WebElement notificationsMenu;
101   
102    /**
103    * Used to scroll the page to the top before accessing the floating menu.
104    */
105    @FindBy(id = "companylogo")
106    protected WebElement logo;
107   
108    /**
109    * Note: when reusing instances of BasePage, the constructor is not doing the work anymore and the
110    * waitUntilPageJSIsLoaded() method needs to be executed manually, when needed.
111    * <p>
112    * Note2: Never call the constructor before navigating to the page you need to test first.
113    */
 
114  1343 toggle public BasePage()
115    {
116  1343 super();
117  1343 waitUntilPageJSIsLoaded();
118    }
119   
 
120  0 toggle public String getPageTitle()
121    {
122  0 return getDriver().getTitle();
123    }
124   
125    // TODO I think this should be in the AbstractTest instead -cjdelisle
 
126  6 toggle public String getPageURL()
127    {
128  6 return getDriver().getCurrentUrl();
129    }
130   
131    /**
132    * @param metaName the name of the XWiki document metadata
133    * @return the value of the specified XWiki document metadata for the current XWiki document
134    * @see #getHTMLMetaDataValue(String)
135    */
 
136  49 toggle public String getMetaDataValue(String metaName)
137    {
138  49 return getDriver().findElement(By.xpath("/html")).getAttribute("data-xwiki-" + metaName);
139    }
140   
141    /**
142    * @param metaName the name of the HTML meta field
143    * @return the value of the requested HTML meta field with from the current page
144    * @since 7.2RC1
145    */
 
146  6 toggle public String getHTMLMetaDataValue(String metaName)
147    {
148  6 return getDriver().findElement(By.xpath("//meta[@name='" + metaName + "']")).getAttribute("content");
149    }
150   
151    /**
152    * @return true if we are currently logged in, false otherwise
153    */
 
154  19 toggle public boolean isAuthenticated()
155    {
156  19 return getDriver().hasElementWithoutWaiting(By.id("tmUser"));
157    }
158   
159    /**
160    * Determine if the current page is a new document.
161    *
162    * @return true if the document is new, false otherwise
163    */
 
164  2 toggle public boolean isNewDocument()
165    {
166  2 return (Boolean) ((JavascriptExecutor) getDriver()).executeScript("return XWiki.docisnew");
167    }
168   
169    /**
170    * Perform a click on a "edit menu" sub-menu entry.
171    *
172    * @param id The id of the entry to follow
173    */
 
174  67 toggle protected void clickEditSubMenuEntry(String id)
175    {
176  67 clickSubMenuEntryFromMenu(By.xpath("//div[@id='tmEdit']/a[contains(@role, 'button')]"), id);
177    }
178   
179    /**
180    * Performs a click on the "edit" button.
181    */
 
182  24 toggle public void edit()
183    {
184  24 WebElement editMenuButton =
185    getDriver().findElement(By.xpath("//div[@id='tmEdit']/a[contains(@role, 'button')]"));
186    // The edit button is not the same depending on whether the user is advanced or not
187  24 if ("dropdown".equals(editMenuButton.getAttribute("data-toggle"))) {
188  20 clickEditSubMenuEntry("tmEditDefault");
189    } else {
190  4 editMenuButton.click();
191    }
192    }
193   
194    /**
195    * Gets a string representation of the URL for editing the page.
196    */
 
197  0 toggle public String getEditURL()
198    {
199  0 return getDriver().findElement(By.xpath("//div[@id='tmEdit']//a")).getAttribute("href");
200    }
201   
202    /**
203    * Performs a click on the "edit wiki" entry of the content menu.
204    */
 
205  16 toggle public WikiEditPage editWiki()
206    {
207  16 clickEditSubMenuEntry("tmEditWiki");
208  16 return new WikiEditPage();
209    }
210   
211    /**
212    * Performs a click on the "edit wysiwyg" entry of the content menu.
213    */
 
214  1 toggle public WYSIWYGEditPage editWYSIWYG()
215    {
216  1 clickEditSubMenuEntry("tmEditWysiwyg");
217  1 return new WYSIWYGEditPage();
218    }
219   
220    /**
221    * Performs a click on the "edit inline" entry of the content menu.
222    */
 
223  7 toggle public <T extends InlinePage> T editInline()
224    {
225  7 clickEditSubMenuEntry("tmEditInline");
226  7 return createInlinePage();
227    }
228   
229    /**
230    * Can be overridden to return extended {@link InlinePage}.
231    */
 
232  6 toggle @SuppressWarnings("unchecked")
233    protected <T extends InlinePage> T createInlinePage()
234    {
235  6 return (T) new InlinePage();
236    }
237   
238    /**
239    * Performs a click on the "edit acces rights" entry of the content menu.
240    */
 
241  1 toggle public RightsEditPage editRights()
242    {
243  1 clickEditSubMenuEntry("tmEditRights");
244  1 return new RightsEditPage();
245    }
246   
247    /**
248    * Performs a click on the "edit objects" entry of the content menu.
249    */
 
250  17 toggle public ObjectEditPage editObjects()
251    {
252  17 clickEditSubMenuEntry("tmEditObject");
253  17 return new ObjectEditPage();
254    }
255   
256    /**
257    * Performs a click on the "edit class" entry of the content menu.
258    */
 
259  5 toggle public ClassEditPage editClass()
260    {
261  5 clickEditSubMenuEntry("tmEditClass");
262  5 return new ClassEditPage();
263    }
264   
265    /**
266    * @since 3.2M3
267    */
 
268  14 toggle public void sendKeys(CharSequence... keys)
269    {
270  14 this.mainContainerDiv.sendKeys(keys);
271    }
272   
273    /**
274    * Waits until the page has loaded. Normally we don't need to call this method since a click in Selenium2 is a
275    * blocking call. However there are cases (such as when using a shortcut) when we asynchronously load a page.
276    *
277    * @return this page
278    * @since 3.2M3
279    */
 
280  26 toggle public BasePage waitUntilPageIsLoaded()
281    {
282  26 getDriver().waitUntilElementIsVisible(By.id("footerglobal"));
283  26 return this;
284    }
285   
286    /**
287    * @since 7.2M3
288    */
 
289  11 toggle public void toggleDrawer()
290    {
291  11 By drawer = By.id("tmDrawer");
292  11 if (getDriver().findElementWithoutWaiting(drawer).isDisplayed()) {
293    // The drawer is visible, so we close it by clicking outside the drawer
294  0 this.mainContainerDiv.click();
295  0 getDriver().waitUntilElementDisappears(drawer);
296    } else {
297    // The drawer is not visible, so we open it
298  11 this.drawerActivator.click();
299  11 getDriver().waitUntilElementIsVisible(drawer);
300    }
301    }
302   
303    /**
304    * @since 7.2M3
305    */
 
306  4 toggle public void toggleActionMenu()
307    {
308  4 this.moreActionsMenu.click();
309    }
310   
311    /**
312    * @since 7.0RC1
313    */
 
314  1 toggle public void clickMoreActionsSubMenuEntry(String id)
315    {
316  1 clickSubMenuEntryFromMenu(By.xpath("//div[@id='tmMoreActions']/a[contains(@role, 'button')]"), id);
317    }
318   
319    /**
320    * @since 7.3M2
321    */
 
322  14 toggle public void clickAdminActionsSubMenuEntry(String id)
323    {
324  14 clickSubMenuEntryFromMenu(By.xpath("//div[@id='tmAdminActions']/a[contains(@role, 'button')]"), id);
325    }
326   
327    /**
328    * @since 7.0RC1
329    */
 
330  82 toggle private void clickSubMenuEntryFromMenu(By menuBy, String id)
331    {
332    // Open the parent Menu
333  82 getDriver().findElement(menuBy).click();
334    // Wait for the submenu entry to be visible
335  82 getDriver().waitUntilElementIsVisible(By.id(id));
336    // Click on the specified entry
337  82 getDriver().findElement(By.id(id)).click();
338    }
339   
340    /**
341    * @return {@code true} if the screen is extra small (as defined by Bootstrap), {@code false} otherwise
342    */
 
343  0 toggle private boolean isExtraSmallScreen()
344    {
345  0 return getDriver().manage().window().getSize().getWidth() < 768;
346    }
347   
 
348  0 toggle private By getTopMenuToggleSelector(String menuId)
349    {
350  0 String side = isExtraSmallScreen() ? "left" : "right";
351  0 return By.xpath("//li[@id='" + menuId + "']//a[contains(@class, 'dropdown-split-" + side + "')]");
352    }
353   
354    /**
355    * @since 4.5M1
356    */
 
357  15 toggle public CreatePagePage createPage()
358    {
359  15 this.tmCreate.click();
360  15 return new CreatePagePage();
361    }
362   
363    /**
364    * @since 4.5M1
365    */
 
366  3 toggle public CopyPage copy()
367    {
368  3 clickAdminActionsSubMenuEntry("tmActionCopy");
369  3 return new CopyPage();
370    }
371   
 
372  2 toggle public RenamePage rename()
373    {
374  2 clickAdminActionsSubMenuEntry("tmActionRename");
375  2 return new RenamePage();
376    }
377   
378    /**
379    * @since 4.5M1
380    */
 
381  9 toggle public ConfirmationPage delete()
382    {
383  9 clickAdminActionsSubMenuEntry("tmActionDelete");
384  9 return new ConfirmationPage();
385    }
386   
387    /**
388    * @since 4.5M1
389    */
 
390  1 toggle public boolean canDelete()
391    {
392  1 toggleActionMenu();
393    // Don't wait here since test can use this method to verify that there's no Delete right on the current page
394    // and calling hasElement() would incurr the wait timeout.
395  1 boolean canDelete = getDriver().hasElementWithoutWaiting(By.id("tmActionDelete"));
396  1 toggleActionMenu();
397  1 return canDelete;
398    }
399   
400    /**
401    * @since 4.5M1
402    */
 
403  1 toggle public void watchDocument()
404    {
405  1 toggleNotificationsMenu();
406  1 this.watchDocumentLink.click();
407  1 toggleActionMenu();
408    }
409   
410    /**
411    * @since 4.5M1
412    */
 
413  0 toggle public boolean hasLoginLink()
414    {
415    // Note that we cannot test if the loginLink field is accessible since we're using an AjaxElementLocatorFactory
416    // and thus it would wait 15 seconds before considering it's not accessible.
417  0 return !getDriver().findElementsWithoutWaiting(By.id("tmLogin")).isEmpty();
418    }
419   
420    /**
421    * @since 4.5M1
422    */
 
423  4 toggle public LoginPage login()
424    {
425  4 toggleDrawer();
426  4 this.loginLink.click();
427  4 return new LoginPage();
428    }
429   
430    /**
431    * @since 4.5M1
432    */
 
433  2 toggle public String getCurrentUser()
434    {
435  2 return this.userLink.getText();
436    }
437   
438    /**
439    * @since 9.0RC1
440    */
 
441  0 toggle public List<Locale> getLocales()
442    {
443  0 List<WebElement> elements =
444    getDriver().findElementsWithoutWaiting(By.xpath("//ul[@id='tmLanguages_menu']/li/a"));
445  0 List<Locale> locales = new ArrayList<>(elements.size());
446  0 for (WebElement element : elements) {
447  0 String href = element.getAttribute("href");
448  0 Matcher matcher = Pattern.compile(".*\\?.*language=([^=&]*)").matcher(href);
449  0 if (matcher.matches()) {
450  0 String locale = matcher.group(1);
451  0 locales.add(LocaleUtils.toLocale(locale));
452    }
453    }
454   
455  0 return locales;
456    }
457   
458    /**
459    * @since 9.0RC1
460    */
 
461  0 toggle public ViewPage clickLocale(Locale locale)
462    {
463    // Open drawer
464  0 toggleDrawer();
465   
466    // Open Languages
467  0 WebElement languagesElement = getDriver().findElementWithoutWaiting(By.xpath("//a[@id='tmLanguages']"));
468  0 languagesElement.click();
469   
470    // Click passed locale
471  0 WebElement localeElement = getDriver().findElementWithoutWaiting(
472    By.xpath("//ul[@id='tmLanguages_menu']/li/a[contains(@href,'language=" + locale + "')]"));
473  0 localeElement.click();
474   
475  0 return new ViewPage();
476    }
477   
478    /**
479    * @since 4.5M1
480    */
 
481  3 toggle public void logout()
482    {
483  3 toggleDrawer();
484  3 getDriver().findElement(By.id("tmLogout")).click();
485    // Update the CSRF token because the context user has changed (it's guest user now). Otherwise, APIs like
486    // TestUtils#createUser*(), which expect the currently cached token to be valid, will fail because they would be
487    // using the token of the previously logged in user.
488  3 getUtil().recacheSecretToken();
489    }
490   
491    /**
492    * @since 4.5M1
493    */
 
494  0 toggle public RegistrationPage register()
495    {
496  0 toggleDrawer();
497  0 this.registerLink.click();
498  0 return new RegistrationPage();
499    }
500   
501    /**
502    * @since 4.5M1
503    */
 
504  42 toggle public String getDocumentTitle()
505    {
506  42 return this.documentTitle.getText();
507    }
508   
509    /**
510    * @since 4.5M1
511    */
 
512  2 toggle public void watchSpace()
513    {
514  2 toggleNotificationsMenu();
515  2 this.watchSpaceLink.click();
516  2 toggleNotificationsMenu();
517    }
518   
519    /**
520    * @since 6.0M1
521    */
 
522  1 toggle public void watchWiki()
523    {
524  1 toggleNotificationsMenu();
525  1 this.watchWikiLink.click();
526  1 toggleNotificationsMenu();
527    }
528   
529    /**
530    * Waits for the javascript libraries and their plugins that need to load before the UI's elements can be used
531    * safely.
532    * <p>
533    * Subclassed should override this method and add additional checks needed by their logic.
534    *
535    * @since 6.2
536    */
 
537  1347 toggle public void waitUntilPageJSIsLoaded()
538    {
539    // Prototype
540  1347 getDriver().waitUntilJavascriptCondition("return window.Prototype != null && window.Prototype.Version != null");
541   
542    // JQuery and dependencies
543    // JQuery dropdown plugin needed for the edit button's dropdown menu.
544  1347 getDriver().waitUntilJavascriptCondition("return window.jQuery != null && window.jQuery().dropdown != null");
545    }
546   
547    /**
548    * Opens the viewer that lists the children of the current page.
549    *
550    * @return the viewer that lists the child pages
551    * @since 7.3RC1
552    */
 
553  1 toggle public ChildrenViewer viewChildren()
554    {
555  1 toggleActionMenu();
556  1 this.childrenLink.click();
557  1 return new ChildrenViewer();
558    }
559   
560    /**
561    * Says if the notifications menu is present (it is displayed only if it has some content).
562    *
563    * @return either or not the notifications menu is present
564    * @since 7.4M1
565    */
 
566  0 toggle public boolean hasNotificationsMenu()
567    {
568  0 return getDriver().hasElementWithoutWaiting(By.id("tmNotifications"));
569    }
570   
571    /**
572    * Open/Close the notifications menu.
573    *
574    * @since 7.4M1
575    */
 
576  7 toggle public void toggleNotificationsMenu()
577    {
578  7 boolean hasMenu = isNotificationsMenuOpen();
579  7 this.notificationsMenu.click();
580  7 if (hasMenu) {
581  3 getDriver().waitUntilElementDisappears(this.notificationsMenu, By.className("dropdown-menu"));
582    } else {
583  4 getDriver().waitUntilElementIsVisible(this.notificationsMenu, By.className("dropdown-menu"));
584    }
585    }
586   
587    /**
588    * @return true if the notifications menu is open
589    * @since 7.4M1
590    */
 
591  7 toggle public boolean isNotificationsMenuOpen()
592    {
593  7 return this.notificationsMenu.findElement(By.className("dropdown-menu")).isDisplayed();
594    }
595   
596    /**
597    * @return the text of uncaught errors
598    * @since 8.0M1
599    */
 
600  0 toggle public String getErrorContent()
601    {
602  0 return getDriver()
603    .findElementWithoutWaiting(By.xpath("//div[@id = 'mainContentArea']/pre[contains(@class, 'xwikierror')]"))
604    .getText();
605    }
606    }