/* * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Sun Seng David TAN <stan@nuxeo.com> * Antoine Taillefer * Gabriel Barata */ package org.nuxeo.functionaltests.pages; import static org.junit.Assert.assertEquals; import static org.nuxeo.functionaltests.Constants.FILE_TYPE; import static org.nuxeo.functionaltests.Constants.FOLDER_TYPE; import static org.nuxeo.functionaltests.Constants.FORUM_TYPE; import static org.nuxeo.functionaltests.Constants.NOTE_TYPE; import static org.nuxeo.functionaltests.Constants.WORKSPACES_TITLE; import java.io.IOException; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.functionaltests.AjaxRequestManager; import org.nuxeo.functionaltests.Constants; import org.nuxeo.functionaltests.Locator; import org.nuxeo.functionaltests.Required; import org.nuxeo.functionaltests.fragment.AddAllToCollectionForm; import org.nuxeo.functionaltests.fragment.AddToCollectionForm; import org.nuxeo.functionaltests.pages.actions.ContextualActions; import org.nuxeo.functionaltests.pages.admincenter.AdminCenterBasePage; import org.nuxeo.functionaltests.pages.forms.CollectionCreationFormPage; import org.nuxeo.functionaltests.pages.forms.DublinCoreCreationDocumentFormPage; import org.nuxeo.functionaltests.pages.forms.FileCreationFormPage; import org.nuxeo.functionaltests.pages.forms.FolderCreationFormPage; import org.nuxeo.functionaltests.pages.forms.ForumCreationFormPage; import org.nuxeo.functionaltests.pages.forms.NoteCreationFormPage; import org.nuxeo.functionaltests.pages.forms.WorkspaceCreationFormPage; import org.nuxeo.functionaltests.pages.search.SearchPage; import org.nuxeo.functionaltests.pages.tabs.CollectionContentTabSubPage; import org.nuxeo.functionaltests.pages.tabs.CommentsTabSubPage; import org.nuxeo.functionaltests.pages.tabs.ContentTabSubPage; import org.nuxeo.functionaltests.pages.tabs.EditTabSubPage; import org.nuxeo.functionaltests.pages.tabs.FilesTabSubPage; import org.nuxeo.functionaltests.pages.tabs.ForumTabSubPage; import org.nuxeo.functionaltests.pages.tabs.HistoryTabSubPage; import org.nuxeo.functionaltests.pages.tabs.ManageTabSubPage; import org.nuxeo.functionaltests.pages.tabs.PermissionsSubPage; import org.nuxeo.functionaltests.pages.tabs.PublishTabSubPage; import org.nuxeo.functionaltests.pages.tabs.RelationTabSubPage; import org.nuxeo.functionaltests.pages.tabs.SectionContentTabSubPage; import org.nuxeo.functionaltests.pages.tabs.SectionsContentTabSubPage; import org.nuxeo.functionaltests.pages.tabs.SummaryTabSubPage; import org.nuxeo.functionaltests.pages.tabs.TopicTabSubPage; import org.nuxeo.functionaltests.pages.tabs.WorkflowTabSubPage; import org.nuxeo.functionaltests.pages.tabs.WorkspacesContentTabSubPage; import org.nuxeo.functionaltests.pages.workspace.WorkspaceHomePage; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.StaleElementReferenceException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; /** * The nuxeo main document base page * * @author Sun Seng David TAN <stan@nuxeo.com> */ public class DocumentBasePage extends AbstractPage { private static final Log log = LogFactory.getLog(DocumentBasePage.class); /** * Exception occurred a user is expected to be connected but it isn't. */ public class UserNotConnectedException extends Exception { private static final long serialVersionUID = 1L; public UserNotConnectedException(String username) { super("The user " + username + " is expected to be connected but isn't"); } } @FindBy(xpath = "//form[@id='breadcrumbForm']") public WebElement breadcrumbForm; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Content']") public WebElement contentTabLink; public ContextualActions contextualActions; @FindBy(className = "creator") public WebElement currentDocumentContributor; @FindBy(className = "documentDescription") public WebElement currentDocumentDescription; @FindBy(xpath = "//form[@id='document_header_layout_form']//h1") public WebElement currentDocumentTitle; @FindBy(className = "currentDocumentDescription") public WebElement currentFolderishDescription; @FindBy(linkText = "WORKSPACE") public WebElement documentManagementLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Edit']") public WebElement editTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Files']") public WebElement filesTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Permissions']") public WebElement permissionsTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='History']") public WebElement historyTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Manage']") public WebElement manageTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Relations']") public WebElement relationTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Summary']") public WebElement summaryTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Publish']") public WebElement publishTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Comments']") public WebElement commentsTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Forum']") public WebElement forumTabLink; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Topic']") public WebElement topicTabLink; @Required @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']") public WebElement tabsBar; @FindBy(xpath = "//div[@id='nxw_documentTabs_panel']//a/span[text()='Workflow']") public WebElement workflowLink; @Required @FindBy(id = "nxw_userMenuActions_panel") public WebElement userMenuActions; @Required @FindBy(linkText = "HOME") public WebElement homePageLink; @Required @FindBy(linkText = "SEARCH") public WebElement searchPageLink; public DocumentBasePage(WebDriver driver) { super(driver); } /** * Check if the title of the current document page is equal to the {@code expectedTitle}. * * @param expectedTitle the expected title */ public void checkDocTitle(String expectedTitle) { assertEquals(expectedTitle, getCurrentDocumentTitle()); } /** * Check if the user is connected by looking for an element with the {@code username} as a class. * * @param username the username * @throws UserNotConnectedException */ public void checkUserConnected(String username) throws UserNotConnectedException { try { findElementWithTimeout(By.cssSelector("span." + username)); } catch (NoSuchElementException e) { throw new UserNotConnectedException(username); } } /** * @since 7.10 */ public void clickOnDocumentTabLink(WebElement tabLink) { clickOnDocumentTabLink(tabLink, useAjaxTabs()); } /** * Clicks on given tab element, detecting begin and end of potential ajax request. * * @since 8.1 */ public void clickOnDocumentTabLink(WebElement tabLink, boolean useAjax) { clickOnTabIfNotSelected("nxw_documentTabs_panel", tabLink, useAjax); } public AdminCenterBasePage getAdminCenter() { findElementWaitUntilEnabledAndClick(By.linkText("ADMIN")); return asPage(AdminCenterBasePage.class); } /** * Click on the content tab and return the subpage of this page. */ public ContentTabSubPage getContentTab() { return getContentTab(ContentTabSubPage.class); } public <T> T getContentTab(Class<T> tabClass) { clickOnDocumentTabLink(contentTabLink); return asPage(tabClass); } public CollectionContentTabSubPage getCollectionContentTab() { return getContentTab(CollectionContentTabSubPage.class); } public ContextualActions getContextualActions() { return asPage(ContextualActions.class); } /** * @since 7.3 */ public List<WebElement> getBlobActions(int index) { return findElementsWithTimeout(By.xpath("(//div[@class='actionsColumn'])[" + (index + 1) + "]//a")); } public String getCurrentContributors() { return currentDocumentContributor.getText(); } public String getCurrentDocumentDescription() { return currentDocumentDescription.getText(); } public String getCurrentDocumentTitle() { return currentDocumentTitle.getText(); } public String getCurrentFolderishDescription() { return currentFolderishDescription.getText(); } public List<String> getCurrentStates() { return findElementsWithTimeout(By.className("sticker")).stream() .map(WebElement::getText) .collect(Collectors.toList()); } /** * @deprecated since 7.3: use {@link #goToWorkspaces()} instead */ @Deprecated public DocumentBasePage getDocumentManagement() { return goToWorkspaces(); } public EditTabSubPage getEditTab() { return getEditTab(EditTabSubPage.class); } /** * @since 8.3 */ public <T extends EditTabSubPage> T getEditTab(Class<T> tabClass) { clickOnDocumentTabLink(editTabLink); return asPage(tabClass); } public FilesTabSubPage getFilesTab() { clickOnDocumentTabLink(filesTabLink); return asPage(FilesTabSubPage.class); } public PermissionsSubPage getPermissionsTab() { // not ajaxified clickOnDocumentTabLink(permissionsTabLink, false); return asPage(PermissionsSubPage.class); } public HistoryTabSubPage getHistoryTab() { clickOnDocumentTabLink(historyTabLink); return asPage(HistoryTabSubPage.class); } public ManageTabSubPage getManageTab() { clickOnDocumentTabLink(manageTabLink); return asPage(ManageTabSubPage.class); } public NavigationSubPage getNavigationSubPage() { return asPage(NavigationSubPage.class); } public RelationTabSubPage getRelationTab() { clickOnDocumentTabLink(relationTabLink); return asPage(RelationTabSubPage.class); } public SummaryTabSubPage getSummaryTab() { clickOnDocumentTabLink(summaryTabLink); return asPage(SummaryTabSubPage.class); } /** * @since 8.3 */ public CommentsTabSubPage getCommentsTab() { clickOnDocumentTabLink(commentsTabLink); return asPage(CommentsTabSubPage.class); } /** * @since 8.3 */ public ForumTabSubPage getForumTab() { clickOnDocumentTabLink(forumTabLink); return asPage(ForumTabSubPage.class); } /** * @since 8.3 */ public TopicTabSubPage getTopicTab() { clickOnDocumentTabLink(topicTabLink); return asPage(TopicTabSubPage.class); } /** * @since 8.3 */ public PublishTabSubPage getPublishTab() { clickOnDocumentTabLink(publishTabLink); return asPage(PublishTabSubPage.class); } /** * @since 5.7 */ public UserHomePage getUserHome() { findElementWaitUntilEnabledAndClick(By.linkText("HOME")); UserHomePage page = asPage(UserHomePage.class); // make sure we're back on the dashboard tab return page.goToDashboard(); } public WorkflowTabSubPage getWorkflow() { clickOnDocumentTabLink(workflowLink); return asPage(WorkflowTabSubPage.class); } /** * For workspace type, the content tab is a bit different. */ public WorkspacesContentTabSubPage getWorkspacesContentTab() { clickOnDocumentTabLink(contentTabLink); return asPage(WorkspacesContentTabSubPage.class); } public DocumentBasePage goToDocumentByBreadcrumb(String documentTitle) { clickOnBreadcrumbElement(driver, documentTitle); return asPage(DocumentBasePage.class); } /** * Makes a breadcrumb element usable, to workaround issues in tests when resolution is too small, see NXP-19710. * * @since 8.3 */ public static void makeBreadcrumbUsable(WebDriver driver) { log.warn("Removing header elements to make breadcrumb usable"); JavascriptExecutor executor = (JavascriptExecutor) driver; // ugly hack for NXP-19710: prevent header elements from hiding breadcrumb links when window is too small String[] ids = { "logolink", "nxw_mainTabs_panel", "nxw_userActions_panel", "nxw_userMenuActions_panel", "nxw_headerSearch_panel" }; for (String id : ids) { try { // make sure element is loaded driver.findElement(By.id(id)); // remove it executor.executeScript("return document.getElementById('" + id + "').remove()"); } catch (NoSuchElementException e) { // ignore } } } /** * Clicks on a breadcrumb element. * * @since 8.3 */ public static void clickOnBreadcrumbElement(WebDriver driver, String documentTitle) { makeBreadcrumbUsable(driver); WebElement breadcrumb = driver.findElement(By.xpath("//form[@id='breadcrumbForm']")); Locator.waitUntilEnabledAndClick(breadcrumb.findElement(By.linkText(documentTitle))); } private static final String ADD_TO_COLLECTION_UPPER_ACTION_ID = "nxw_addToCollectionAction_form:nxw_addToCollectionAction_link"; private static final String ADD_ALL_TO_COLLECTION_ACTION_ID = "document_content_buttons:nxw_addSelectedToCollectionAction_form:nxw_addSelectedToCollectionAction_link"; @FindBy(id = ADD_TO_COLLECTION_UPPER_ACTION_ID) private WebElement addToCollectionUpperAction; @FindBy(id = ADD_ALL_TO_COLLECTION_ACTION_ID) private WebElement addAllToCollectionAction; /** * @since 5.9.3 */ public AddToCollectionForm getAddToCollectionPopup() { AjaxRequestManager arm = new AjaxRequestManager(driver); arm.begin(); waitUntilEnabledAndClick(addToCollectionUpperAction); arm.end(); WebElement elt = AbstractPage.getFancyBoxContent(); return getWebFragment(elt, AddToCollectionForm.class); } /** * @since 5.9.3 * @deprecated since 9.1 use actions from {@link ContentTabSubPage} instead. */ @Deprecated public AddAllToCollectionForm getAddAllToCollectionPopup() { Locator.waitUntilGivenFunctionIgnoring( driver -> StringUtils.isBlank( driver.findElement(By.id(ADD_ALL_TO_COLLECTION_ACTION_ID)).getAttribute("disabled")), StaleElementReferenceException.class); AjaxRequestManager arm = new AjaxRequestManager(driver); arm.begin(); findElementWaitUntilEnabledAndClick(By.id(ADD_ALL_TO_COLLECTION_ACTION_ID)); arm.end(); WebElement elt = AbstractPage.getFancyBoxContent(); return getWebFragment(elt, AddAllToCollectionForm.class); } public boolean isAddToCollectionUpperActionAvailable() { try { driver.findElement(By.id(ADD_TO_COLLECTION_UPPER_ACTION_ID)); return true; } catch (final NoSuchElementException e) { return false; } } /** * @since 5.9.3 */ public void popupUserMenuActions() { Locator.findElementWaitUntilEnabledAndClick(userMenuActions, By.id("nxw_userMenuActions_dropDownMenu")); Locator.waitUntilGivenFunctionIgnoring( driver -> !userMenuActions.findElement(By.xpath("//ul[@class='actionSubList']")) .getAttribute("style") .equals("display: none;"), StaleElementReferenceException.class); } /** * @since 5.9.3 */ public DocumentBasePage switchToPersonalWorkspace() { popupUserMenuActions(); findElementWaitUntilEnabledAndClick(By.linkText("Personal Workspace")); return asPage(DocumentBasePage.class); } /** * @since 5.9.3 */ public DocumentBasePage switchToDocumentBase() { popupUserMenuActions(); findElementWaitUntilEnabledAndClick(By.linkText("Back to Document Base")); return asPage(DocumentBasePage.class); } /** * @since 5.9.3 */ public HomePage goToHomePage() { waitUntilEnabledAndClick(homePageLink); return asPage(HomePage.class); } /** * @since 6.0 */ public SearchPage goToSearchPage() { waitUntilEnabledAndClick(searchPageLink); return asPage(SearchPage.class); } /** * @since 7.3 */ public WorkspaceHomePage goToWorkspaces() { waitUntilEnabledAndClick(documentManagementLink); return asPage(WorkspaceHomePage.class); } /** * Returns true if given element representing a main tab is selected in UI. * * @since 7.3 */ public boolean isMainTabSelected(WebElement tab) { WebElement elt = Locator.findParentTag(tab, "li"); String css = elt.getAttribute("class"); return css != null && css.contains("selected"); } /** * Creates a Workspace from this page. * * @param workspaceTitle the workspace title * @param workspaceDescription the workspace description * @return the created Workspace page * @since 8.3 */ public DocumentBasePage createWorkspace(String workspaceTitle, String workspaceDescription) { // Go to Workspaces DocumentBasePage workspacesPage = getNavigationSubPage().goToDocument(WORKSPACES_TITLE); // Get Workspace creation form page WorkspaceCreationFormPage workspaceCreationFormPage = workspacesPage.getWorkspacesContentTab() .getWorkspaceCreatePage(); // Create Workspace DocumentBasePage workspacePage = workspaceCreationFormPage.createNewWorkspace(workspaceTitle, workspaceDescription); return workspacePage; } /** * Creates a Section from this page. * * @param sectionTitle the section title * @param sectionDescription the section description * @return the created Section page * @since 8.3 */ public DocumentBasePage createSection(String sectionTitle, String sectionDescription) { getNavigationSubPage().goToDocument(Constants.SECTIONS_TITLE); DublinCoreCreationDocumentFormPage sectionCreationPage = asPage( SectionsContentTabSubPage.class).getSectionCreatePage(); return sectionCreationPage.createDocument(sectionTitle, sectionDescription); } /** * Deletes the Workspace with title {@code workspaceTitle} from this page. * * @param workspaceTitle the workspace title * @since 8.3 */ public void deleteWorkspace(String workspaceTitle) { // Go to Workspaces DocumentBasePage workspacesPage = getNavigationSubPage().goToDocument(WORKSPACES_TITLE); // Delete the Workspace workspacesPage.getContentTab().removeDocument(workspaceTitle); } /** * @since 8.3 */ public FolderDocumentBasePage createFolder(String folderTitle, String folderDescription) { // Get Folder creation form page FolderCreationFormPage folderCreationFormPage = getContentTab().getDocumentCreatePage(FOLDER_TYPE, FolderCreationFormPage.class); // Create Folder FolderDocumentBasePage folderPage = folderCreationFormPage.createFolderDocument(folderTitle, folderDescription); return folderPage; } /** * Creates a File from this page. * * @param fileTitle the file title * @param fileDescription the file description * @param uploadBlob true if a blob needs to be uploaded (temporary file created for this purpose) * @param filePrefix the file prefix * @param fileSuffix the file suffix * @param fileContent the file content * @return the created File page * @throws IOException if temporary file creation fails * @since 8.3 */ public FileDocumentBasePage createFile(String fileTitle, String fileDescription, boolean uploadBlob, String filePrefix, String fileSuffix, String fileContent) throws IOException { // Get File creation form page FileCreationFormPage fileCreationFormPage = getContentTab().getDocumentCreatePage(FILE_TYPE, FileCreationFormPage.class); // Create File FileDocumentBasePage filePage = fileCreationFormPage.createFileDocument(fileTitle, fileDescription, uploadBlob, filePrefix, fileSuffix, fileDescription); return filePage; } /** * Creates a Collections container from this page. * * @param collectionsTitle the Collections container title * @param fileDescription the collections description * @return the created Collections page * @since 8.3 */ public DocumentBasePage createCollections(String collectionsTitle, String fileDescription) { DublinCoreCreationDocumentFormPage dublinCoreDocumentFormPage = getContentTab().getDocumentCreatePage( "Collections", DublinCoreCreationDocumentFormPage.class); // Create File DocumentBasePage documentBasePage = dublinCoreDocumentFormPage.createDocument(collectionsTitle, fileDescription); return documentBasePage; } /** * Creates a Collection from this page. * * @param collectionsTitle the Collections container title * @param fileDescription the collection description * @return the created Collections page * @since 8.3 */ public CollectionContentTabSubPage createCollection(String collectionsTitle, String fileDescription) { CollectionCreationFormPage collectionCreationFormPage = getContentTab().getDocumentCreatePage("Collection", CollectionCreationFormPage.class); // Create File CollectionContentTabSubPage documentBasePage = collectionCreationFormPage.createDocument(collectionsTitle, fileDescription); return documentBasePage; } /** * Creates a Note from this page. * * @param noteTitle the note title * @param noteDescription the note description * @param defineNote true if the content of the note needs to be defined * @param noteContent the content of the note * @return the created note page. * @throws IOException * @since 8.3 */ public NoteDocumentBasePage createNote(String noteTitle, String noteDescription, boolean defineNote, String noteContent) throws IOException { // Get the Note creation form NoteCreationFormPage noteCreationPage = getContentTab().getDocumentCreatePage(NOTE_TYPE, NoteCreationFormPage.class); // Create a Note NoteDocumentBasePage notePage = noteCreationPage.createNoteDocument(noteTitle, noteDescription, defineNote, noteContent); return notePage; } /** * Creates a forum on this page. * * @param forumTitle the forum title * @param forumDescription the forum description * @return the created forum page * @since 8.3 */ public ForumTabSubPage createForum(String forumTitle, String forumDescription) { // Get a Forum creation form ForumCreationFormPage forumCreationFormPage = getContentTab().getDocumentCreatePage(FORUM_TYPE, ForumCreationFormPage.class); // Create a Forum ForumTabSubPage forumPage = forumCreationFormPage.createForumDocument(forumTitle, forumDescription); return forumPage; } /** * @since 8.3 */ public boolean hasNewRelationLink() { return getRelationTab().hasNewRelationLink(); } /** * @since 8.3 */ public boolean hasFilesTab() { try { return getFilesTab() != null; } catch (NoSuchElementException e) { return false; } } /** * @since 8.3 */ public boolean hasNewButton(boolean isSection) { if (isSection) { return getContentTab(SectionContentTabSubPage.class).hasNewButton(); } return getContentTab().hasNewButton(); } /** * @since 8.3 */ public boolean hasEditTab() { try { return getEditTab() != null; } catch (NoSuchElementException e) { return false; } } /** * @since 8.3 */ public boolean hasNewPermissionsButton() { return getPermissionsTab().hasNewPermissionButton(); } /** * @since 8.3 */ public boolean hasManageTab() { try { return getManageTab() != null; } catch (NoSuchElementException e) { return false; } } }