package com.vaadin.tests.components.combobox; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.testbench.elements.CheckBoxElement; import com.vaadin.testbench.elements.ComboBoxElement; import com.vaadin.tests.tb3.SingleBrowserTest; public class ComboBoxBackEndRequestsTest extends SingleBrowserTest { @Before public void open() { openTestURL("?debug"); } private void open(int pageLength, int items) { openTestURL(ComboBoxBackEndRequests.PAGE_LENGTH_REQUEST_PARAMETER + "=" + pageLength + "&" + ComboBoxBackEndRequests.ITEMS_REQUEST_PARAMETER + "=" + items + "&restartApplication"); } @Test public void testInitialLoad_onlySizeAndFirstItemsRequested() { verifyInitialLoadRequests(); } @Test public void testOpeningDropDown_noRequests() { verifyInitialLoadRequests(); clearLogs(); openPopup(); verifyNumberOrRequests( "opening drop down should not have caused requests", 0); // select something to close popup selectByClick("Item 2"); verifyNumberOrRequests("selecting should have not caused requests", 0); openPopup(); verifyNumberOrRequests( "opening drop down should not have caused requests", 0); } @Test public void testNoPaging_nullSelectionAllowed() { final int pageLength = 0; final int items = 20; open(pageLength, items); verifyInitialLoadRequests(); clearLogs(); openPopup(); verifyNumberOrRequests("extra request expected", 1); // TODO with pageLength 0, the cache strategy forces to fetch the whole // set for opening popup verifyFetchRequest(0, 0, items, null); verifyPopupItems(true, 0, items - 1); // pop up status not shown for only one page! } @Test public void testNoPaging_nullSelectionDisallowed() { final int pageLength = 0; final int items = 20; open(pageLength, items); verifyInitialLoadRequests(); clearLogs(); triggerNullSelectionAllowed(false); openPopup(); verifyNumberOrRequests("extra request expected", 1); // TODO the cache strategy forces to fetch the whole range, instead of // just the missing 40-50 verifyFetchRequest(0, 0, items, null); verifyPopupItems(false, 0, items - 1); // pop up status not shown for only one page! } @Test public void testPagingWorks_nullSelectionAllowed_defaultSizes() { verifyPopupPages(ComboBoxBackEndRequests.DEFAULT_PAGE_LENGTH, ComboBoxBackEndRequests.DEFAULT_NUMBER_OF_ITEMS, true); } @Test public void testPagingWorks_nullSelectionDisallowed_defaultSizes() { triggerNullSelectionAllowed(false); verifyPopupPages(ComboBoxBackEndRequests.DEFAULT_PAGE_LENGTH, ComboBoxBackEndRequests.DEFAULT_NUMBER_OF_ITEMS, false); } @Test public void testInitialPage_pageLengthBiggerThanInitialCache() { // initial request is for 40 items final int pageLength = 50; final int items = 100; open(pageLength, items); verifyNumberOrRequests("three initial requests expected", 3); verifySizeRequest(0, null); verifyFetchRequest(1, 0, 40, null); verifyFetchRequest(2, 40, 60, null); clearLogs(); verifyPopupPages(pageLength, items, true); // browsing through the pages should't have caused more requests verifyNumberOrRequests("no additional requests should have happened", 0); } @Test public void testPagingWorks_nullSelectionAllowed_customSizes() { final int pageLength = 23; final int items = 333; open(pageLength, items); // with null selection allowed verifyPopupPages(pageLength, items, true); } @Test public void testPagingWorks_nullSelectionDisallowed_customSizes() { final int pageLength = 23; final int items = 333; open(pageLength, items); triggerNullSelectionAllowed(false); verifyPopupPages(pageLength, items, false); } @Test @Ignore("cache strategy is still broken for CB") public void testPaging_usesCachedData() { verifyInitialLoadRequests(); clearLogs(); openPopup(); verifyNumberOrRequests( "opening drop down should not have caused requests", 0); nextPage(); verifyNumberOrRequests("next page should have not caused more requests", 0); } @Test public void testPaging_nullSelectionAllowed_correctNumberOfItemsShown() { verifyInitialLoadRequests(); openPopup(); verifyPopupItems(true, 0, 8); nextPage(); verifyPopupItems(false, 9, 18); } @Test public void testPaging_nullSelectionDisallowed_correctNumberOfItemsShown() { verifyInitialLoadRequests(); triggerNullSelectionAllowed(false); openPopup(); verifyPopupItems(false, 0, 9); nextPage(); verifyPopupItems(false, 10, 19); } private void triggerNullSelectionAllowed(boolean expectedValue) { $(CheckBoxElement.class).caption("emptySelectionAllowed").first() .click(); assertEquals(expectedValue, $(CheckBoxElement.class) .caption("emptySelectionAllowed").first().isChecked()); } private void clearLogs() { $(ButtonElement.class).caption("Clear logs").first().click(); verifyNumberOrRequests("logs should have been cleared", 0); } private void selectByClick(String itemText) { $(ComboBoxElement.class).first().getPopupSuggestionElements().stream() .filter(element -> element.getText().equals(itemText)) .findFirst().get().click(); assertEquals("Wrong selected", itemText, $(ComboBoxElement.class).first().getValue()); } private void nextPage() { assertTrue("no next page available", $(ComboBoxElement.class).first().openNextPage()); } private void previousPage() { assertTrue("no previous page available", $(ComboBoxElement.class).first().openPrevPage()); } private void openPopup() { ComboBoxElement element = $(ComboBoxElement.class).first(); assertFalse("popup shouldn't be open", element.isPopupOpen()); element.getSuggestionPopup(); assertTrue("popup should be open", element.isPopupOpen()); } private void verifyPopupPages(int pageSize, int totalItems, boolean nullSelectionAllowed) { final int numberOfPages = new BigDecimal( totalItems + (nullSelectionAllowed ? 1 : 0)) .divide(new BigDecimal(pageSize), RoundingMode.UP) .intValue(); openPopup(); verifyPopupStatus(pageSize, 1, numberOfPages, totalItems, nullSelectionAllowed); for (int i = 2; i <= numberOfPages; i++) { nextPage(); verifyPopupStatus(pageSize, i, numberOfPages, totalItems, nullSelectionAllowed); } assertFalse("there should not be a next page", $(ComboBoxElement.class).first().openNextPage()); if (numberOfPages < 2) { return; } for (int i = numberOfPages - 1; i > 0; i--) { previousPage(); verifyPopupStatus(pageSize, i, numberOfPages, totalItems, nullSelectionAllowed); } assertFalse("there should not be a previous page", $(ComboBoxElement.class).first().openPrevPage()); } private void verifyPopupStatus(int pageSize, int currentPage, int numberOfPages, int totalItems, boolean nullSelectionAllowed) { int start; int end; if (currentPage == 1) { // first page start = 1; end = pageSize - (nullSelectionAllowed ? 1 : 0); } else if (currentPage == numberOfPages) { // last page final int lastPageSize = (totalItems + (nullSelectionAllowed ? 1 : 0)) % pageSize; start = pageSize * (currentPage - 1) + (nullSelectionAllowed ? 0 : 1); end = start + (lastPageSize == 0 ? pageSize : lastPageSize) - 1; } else { // all other start = pageSize * (currentPage - 1) + (nullSelectionAllowed ? 0 : 1); end = pageSize * currentPage - (nullSelectionAllowed ? 1 : 0); } verifyPopupStatus(start, end, totalItems, currentPage, numberOfPages); } private void verifyPopupStatus(int start, int end, int totalItems, int currentPage, int numberOfPages) { WebElement element = findElement(By.className("v-filterselect-status")); assertEquals( "Wrong status text on popup page " + currentPage + "/" + numberOfPages, start + "-" + end + "/" + totalItems, element.getText()); } private void verifyPopupItems(boolean hasNullSelectionItem, int startIndex, int lastIndex) { List<String> popupSuggestions = $(ComboBoxElement.class).first() .getPopupSuggestions(); if (hasNullSelectionItem) { String text = popupSuggestions.remove(0); assertEquals("nullSelectionItem should be visible on page", " ", text); } assertEquals("invalid number of suggestions", lastIndex - startIndex + 1, popupSuggestions.size()); for (int i = startIndex; i <= lastIndex; i++) { assertEquals("unexpected item", "Item " + i, popupSuggestions.get(i - startIndex)); } } private void verifyInitialLoadRequests() { verifyNumberOrRequests("two initial requests expected", 2); verifySizeRequest(0, null); verifyFetchRequest(1, 0, 40, null); } private WebElement verifyFetchRequest(int requestNumber, int startIndex, int size, String filter) { WebElement element = verifyRequest(requestNumber, "FETCH"); // format is 1: FETCH 0 40 _filter_ final String actual = element.getText(); String expected = requestNumber + ": FETCH " + startIndex + " " + size + (filter == null ? "" : " " + filter); assertEquals("invalid fetch data", expected, actual); return element; } private WebElement verifySizeRequest(int index, String filter) { WebElement element = verifyRequest(index, "SIZE"); if (filter != null) { // format is "0: SIZE 0 2147483647 _filter_ String currentFilter = element.getText().split("2147483647")[1]; assertEquals("Wrong filter", filter, currentFilter); } return element; } private WebElement verifyRequest(int index, String queryType) { WebElement element = findElement(By.id(queryType + "-" + index)); return element; } private void verifyNumberOrRequests(String message, int numberOfRequests) { List<WebElement> logRows = getLogRows(); assertEquals(message, numberOfRequests, logRows.size()); } private List<WebElement> getLogRows() { return findElements(By.className("log")); } }