001/*
002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nelson Silva
018 */
019package org.nuxeo.functionaltests.contentView;
020
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023
024import java.util.List;
025import java.util.function.Function;
026
027import org.nuxeo.functionaltests.AbstractTest;
028import org.nuxeo.functionaltests.AjaxRequestManager;
029import org.nuxeo.functionaltests.Assert;
030import org.nuxeo.functionaltests.Locator;
031import org.nuxeo.functionaltests.fragment.WebFragmentImpl;
032import org.nuxeo.functionaltests.pages.DocumentBasePage;
033import org.openqa.selenium.By;
034import org.openqa.selenium.NoSuchElementException;
035import org.openqa.selenium.WebDriver;
036import org.openqa.selenium.WebElement;
037
038/**
039 * Represents a content view element.
040 *
041 * @since 7.1
042 */
043public class ContentViewElement extends WebFragmentImpl {
044
045    private static final String SELECT_ALL_BUTTON_XPATH = "//input[@type=\"checkbox\" and @title=\"Select all / deselect all\"]";
046
047    private static final String CHECK_BOX_XPATH = "td/input[@type=\"checkbox\"]";
048
049    public enum ResultLayout {
050        THUMBNAIL("Thumbnail view"), LISTING("List view");
051
052        private final String title;
053
054        ResultLayout(String title) {
055            this.title = title;
056        }
057    }
058
059    public ContentViewElement(WebDriver driver, WebElement element) {
060        super(driver, element);
061    }
062
063    protected ContentViewElement reload(String id) {
064        return getWebFragment(By.id(id), ContentViewElement.class);
065    }
066
067    /**
068     * @since 9.1
069     */
070    public ContentViewUpperActions getUpperActions() {
071        return AbstractTest.getWebFragment(By.className("contentViewUpperActions"), ContentViewUpperActions.class);
072    }
073
074    /**
075     * @since 9.1
076     */
077    public ContentViewSelectionActions getSelectionActions() {
078        By buttonsId = By.id(String.format("%s_buttons:ajax_selection_buttons", getContentViewType()));
079        Locator.waitUntilElementPresent(buttonsId);
080        return AbstractTest.getWebFragment(buttonsId, ContentViewSelectionActions.class);
081    }
082
083    public PageNavigationControls getPaginationControls() {
084        return AbstractTest.getWebFragment(findElement(By.className("pageNavigationControls")),
085                PageNavigationControls.class);
086    }
087
088    /**
089     * @since 9.1
090     */
091    protected WebElement getFilterInput() {
092        String id = getContentViewId() + "_quickFilterForm:nxl_document_content_filter:nxw_search_title";
093        return findElement(By.id(id));
094    }
095
096    /**
097     * @since 9.1
098     */
099    protected WebElement getFilterButton() {
100        String id = getContentViewId() + "_quickFilterForm:submitFilter";
101        return findElement(By.id(id));
102    }
103
104    /**
105     * @since 9.1
106     */
107    protected WebElement getClearFilterButton() {
108        String id = getContentViewId() + "_resetFilterForm:resetFilter";
109        return findElement(By.id(id));
110    }
111
112    protected String getContentViewId() {
113        String id = getId();
114        if (id.endsWith("_panel")) {
115            return id.substring(0, id.length() - "_panel".length());
116        }
117        return id;
118    }
119
120    /**
121     * @return the content view id with cv_* and _\d* removed. This is useful to get the selection actions.
122     */
123    protected String getContentViewType() {
124        return getContentViewId().replaceFirst("^cv_", "").replaceFirst("_\\d*$", "");
125    }
126
127    protected WebElement getResultsPanel() {
128        String id = getContentViewId() + "_resultsPanel";
129        return getElement().findElement(By.id(id));
130    }
131
132    public WebElement getActionByTitle(String title) {
133        return getUpperActions().getActionByTitle(title);
134    }
135
136    public ContentViewElement switchToResultLayout(ResultLayout layout) {
137        // get id before element is detached from DOM (during next ajax calls)
138        String id = getId();
139        getUpperActions().clickOnActionByTitle(layout.title);
140        return reload(id);
141    }
142
143    /**
144     * Use this method to do navigation actions with reloading of this {@link ContentViewElement}.
145     *
146     * @return the new content view element
147     * @since 9.1
148     */
149    public <T extends DocumentBasePage> ContentViewElement navigation(Function<PageNavigationControls, T> nav) {
150        // get id before element is detached from DOM (during next ajax calls)
151        String id = getId();
152        nav.apply(getPaginationControls());
153        return reload(id);
154    }
155
156    /**
157     * @since 8.3
158     */
159    public List<WebElement> getItems() {
160        ResultLayout layout = getResultLayout();
161        switch (layout) {
162        case THUMBNAIL:
163            return getResultsPanel().findElements(By.xpath(".//div[contains(@class,'bubbleBox')]"));
164        case LISTING:
165        default:
166            return getResultsPanel().findElements(By.xpath("(.//form)[1]//tbody//tr"));
167        }
168    }
169
170    /**
171     * @since 9.1
172     */
173    public WebElement getItemWithTitleAndVersion(String title, String version) {
174        for (WebElement item : getItems()) {
175            String t = item.findElement(By.xpath("td[3]")).getText(); // title
176            String v = item.findElement(By.xpath("td[7]")).getText(); // version
177            if (t.equals(title) && v.equals(version)) {
178                return item;
179            }
180        }
181        throw new NoSuchElementException("No item with title \"" + title + "\" and version " + version);
182    }
183
184    /**
185     * @since 8.3
186     */
187    public void clickOnItemTitle(String title) {
188        Locator.findElementWaitUntilEnabledAndClick(getResultsPanel(), By.linkText(title));
189    }
190
191    /**
192     * @since 9.1
193     */
194    public void clickOnItemTitleAndVersion(String title, String version) {
195        if (getResultLayout() == ResultLayout.THUMBNAIL) {
196            throw new NoSuchElementException("Click on title and version doesn\"t work with thumbnails.");
197        }
198        WebElement item = getItemWithTitleAndVersion(title, version);
199        Locator.findElementWaitUntilEnabledAndClick(item, By.linkText(title));
200    }
201
202    /**
203     * @since 9.1
204     */
205    public void clickOnItemIndex(int index) {
206        if (getResultLayout() == ResultLayout.THUMBNAIL) {
207            throw new NoSuchElementException("Click on index doesn\"t work with thumbnails.");
208        }
209        Locator.waitUntilEnabledAndClick(getItems().get(index).findElement(By.xpath("td[3]/div/a[1]")));
210    }
211
212    /**
213     * @since 8.3
214     */
215    public boolean hasItem(String title) {
216        try {
217            WebElement element = getResultsPanel().findElement(By.linkText(title));
218            return element != null;
219        } catch (NoSuchElementException e) {
220            return false;
221        }
222    }
223
224    /**
225     * Perform filter on the given string.
226     *
227     * @param filter the string to filter
228     * @since 9.1
229     */
230    public ContentViewElement filterDocument(String filter) {
231        WebElement filterInput = getFilterInput();
232        // get id before element is detached from DOM (during next ajax calls)
233        String id = getId();
234        filterInput.clear();
235        filterInput.sendKeys(filter);
236        AjaxRequestManager arm = new AjaxRequestManager(driver);
237        arm.begin();
238        getFilterButton().click();
239        arm.end();
240        Locator.waitUntilElementPresent(By.id(getClearFilterButton().getAttribute("id")));
241        return reload(id);
242    }
243
244    /**
245     * Clear the current filter and refresh content view.
246     *
247     * @since 9.1
248     */
249    public ContentViewElement clearFilter() {
250        WebElement clearFilterButton = getClearFilterButton();
251        // get id before element is detached from DOM (during next ajax calls)
252        String id = getId();
253        String clearFilterButtonId = clearFilterButton.getAttribute("id");
254        AjaxRequestManager arm = new AjaxRequestManager(driver);
255        arm.begin();
256        Locator.waitUntilEnabledAndClick(clearFilterButton);
257        arm.end();
258        Locator.waitUntilElementNotPresent(By.id(clearFilterButtonId));
259        return reload(id);
260    }
261
262    /**
263     * @since 8.3
264     * @deprecated since 9.1 use {@link #selectByTitle(String...)} instead.
265     */
266    @Deprecated
267    public ContentViewElement checkByTitle(String... titles) {
268        // get id before element is detached from DOM (during next ajax calls)
269        String id = getId();
270        List<WebElement> items = getItems();
271        for (WebElement item : items) {
272            for (String title : titles) {
273                try {
274                    item.findElement(By.linkText(title));
275                    AjaxRequestManager arm = new AjaxRequestManager(driver);
276                    arm.begin();
277                    Locator.findElementWaitUntilEnabledAndClick(item, By.xpath(CHECK_BOX_XPATH));
278                    arm.end();
279                    break;
280                } catch (NoSuchElementException e) {
281                    // next
282                }
283            }
284        }
285        return reload(id);
286    }
287
288    /**
289     * @since 9.1
290     */
291    public ContentViewSelectionActions selectByTitle(String... titles) {
292        // get id before element is detached from DOM (during next ajax calls)
293        String id = getId();
294        List<WebElement> items = getItems();
295        for (WebElement item : items) {
296            for (String title : titles) {
297                try {
298                    item.findElement(By.linkText(title));
299                    AjaxRequestManager arm = new AjaxRequestManager(driver);
300                    arm.begin();
301                    WebElement element = item.findElement(By.xpath(CHECK_BOX_XPATH));
302                    assertFalse("Element with title=" + title + " is already selected", element.isSelected());
303                    Locator.scrollAndForceClick(element);
304                    arm.end();
305                    break;
306                } catch (NoSuchElementException e) {
307                    // next
308                }
309            }
310        }
311        return reload(id).getSelectionActions();
312    }
313
314    /**
315     * @since 9.1
316     */
317    public ContentViewElement unselectByTitle(String... titles) {
318        // get id before element is detached from DOM (during next ajax calls)
319        String id = getId();
320        List<WebElement> items = getItems();
321        for (WebElement item : items) {
322            for (String title : titles) {
323                try {
324                    item.findElement(By.linkText(title));
325                    AjaxRequestManager arm = new AjaxRequestManager(driver);
326                    arm.begin();
327                    WebElement element = item.findElement(By.xpath(CHECK_BOX_XPATH));
328                    assertTrue("Element with title=" + title + " is not selected", element.isSelected());
329                    Locator.scrollAndForceClick(element);
330                    arm.end();
331                    break;
332                } catch (NoSuchElementException e) {
333                    // next
334                }
335            }
336        }
337        return reload(id);
338    }
339
340    /**
341     * @since 8.3
342     * @deprecated since 9.1 use {@link #selectByIndex(int...)}/{@link #unselectByIndex(int...)} instead.
343     */
344    @Deprecated
345    public ContentViewElement checkByIndex(int... indexes) {
346        // get id before element is detached from DOM (during next ajax calls)
347        String id = getId();
348        AjaxRequestManager arm = new AjaxRequestManager(driver);
349        for (int i : indexes) {
350            arm.watchAjaxRequests();
351            getItems().get(i).findElement(By.xpath(CHECK_BOX_XPATH)).click();
352            arm.waitForAjaxRequests();
353        }
354        return reload(id);
355    }
356
357    /**
358     * @since 9.1
359     */
360    public ContentViewSelectionActions selectByIndex(int... indexes) {
361        // get id before element is detached from DOM (during next ajax calls)
362        String id = getId();
363        AjaxRequestManager arm = new AjaxRequestManager(driver);
364        for (int i : indexes) {
365            arm.watchAjaxRequests();
366            WebElement element = getItems().get(i).findElement(By.xpath(CHECK_BOX_XPATH));
367            assertFalse("Element with id=" + i + " is already selected", element.isSelected());
368            Locator.scrollAndForceClick(element);
369            arm.waitForAjaxRequests();
370        }
371        return reload(id).getSelectionActions();
372    }
373
374    /**
375     * @since 9.1
376     */
377    public ContentViewElement unselectByIndex(int... indexes) {
378        // get id before element is detached from DOM (during next ajax calls)
379        String id = getId();
380        AjaxRequestManager arm = new AjaxRequestManager(driver);
381        for (int i : indexes) {
382            arm.watchAjaxRequests();
383            WebElement element = getItems().get(i).findElement(By.xpath(CHECK_BOX_XPATH));
384            assertTrue("Element with id=" + i + " is not selected", element.isSelected());
385            Locator.scrollAndForceClick(element);
386            arm.waitForAjaxRequests();
387        }
388        return reload(id);
389    }
390
391    /**
392     * @since 8.3
393     * @deprecated since 9.1 use {@link #selectAll()}/{@link #unselectAll()} instead.
394     */
395    @Deprecated
396    public ContentViewElement checkAllItems() {
397        WebElement selectAll = null;
398        try {
399            selectAll = getResultsPanel().findElement(By.xpath(SELECT_ALL_BUTTON_XPATH));
400        } catch (NoSuchElementException e) {
401            // no item
402        }
403        if (selectAll != null) {
404            // get id before element is detached from DOM (during next ajax call)
405            String id = getId();
406            AjaxRequestManager arm = new AjaxRequestManager(driver);
407            arm.begin();
408            Locator.scrollAndForceClick(selectAll);
409            arm.end();
410            return reload(id);
411        }
412        return this;
413    }
414
415    /**
416     * @since 9.1
417     */
418    public ContentViewSelectionActions selectAll() {
419        WebElement selectAll = getResultsPanel().findElement(By.xpath(SELECT_ALL_BUTTON_XPATH));
420        assertFalse("Select all Element is already selected", selectAll.isSelected());
421        // get id before element is detached from DOM (during next ajax call)
422        String id = getId();
423        AjaxRequestManager arm = new AjaxRequestManager(driver);
424        arm.begin();
425        Locator.scrollAndForceClick(selectAll);
426        arm.end();
427        return reload(id).getSelectionActions();
428    }
429
430    /**
431     * CAUTION You can call this method only after a {@link #selectAll()}.
432     * 
433     * @since 9.1
434     */
435    public ContentViewElement unselectAll() {
436        WebElement selectAll = getResultsPanel().findElement(By.xpath(SELECT_ALL_BUTTON_XPATH));
437        assertTrue("Select all Element is not selected", selectAll.isSelected());
438        // get id before element is detached from DOM (during next ajax call)
439        String id = getId();
440        AjaxRequestManager arm = new AjaxRequestManager(driver);
441        arm.begin();
442        Locator.scrollAndForceClick(selectAll);
443        arm.end();
444        return reload(id);
445    }
446
447    /**
448     * @since 8.3
449     * @deprecated since 9.1 use {@link #getUpperActions()} then
450     *             {@link ContentViewUpperActions#getActionByTitle(String)} instead. Or use select methods directly.
451     */
452    @Deprecated
453    public WebElement getSelectionActionByTitle(String title) {
454        return getUpperActions().getActionByTitle(title);
455    }
456
457    /**
458     * @since 8.4
459     */
460    public ResultLayout getResultLayout() {
461        WebElement element = getElement();
462        WebElement resultsPanel = getResultsPanel();
463        String resultLayoutSelected = ".//span[@class=\"resultLayoutSelection selected\"]/*/img[@alt=\"%s\"]";
464        if (Assert.hasChild(element, By.xpath(String.format(resultLayoutSelected, ResultLayout.THUMBNAIL.title)))
465                || Assert.hasChild(resultsPanel, By.xpath(".//div[contains(@class,'bubbleBox')]"))) {
466            return ResultLayout.THUMBNAIL;
467        } else if (Assert.hasChild(element, By.xpath(String.format(resultLayoutSelected, ResultLayout.LISTING.title)))
468                || Assert.hasChild(resultsPanel, By.xpath(".//table[@class='dataOutput']"))) {
469            return ResultLayout.LISTING;
470        }
471        throw new IllegalStateException("Content view is not listing nor thumbnail.");
472    }
473
474}