Skip to content

Commit

Permalink
Add integration tests checking OpenLayers zoom and rotation buttons o…
Browse files Browse the repository at this point in the history
…f image preview.
  • Loading branch information
thomaslow committed Oct 1, 2024
1 parent 9f3b5ef commit 0852f11
Show file tree
Hide file tree
Showing 3 changed files with 306 additions and 0 deletions.
33 changes: 33 additions & 0 deletions Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,39 @@ class KitodoDetailMap {
$('#thirdColumnWrapper').on('resize', KitodoDetailMap.makeDebounced(this.onResize.bind(this)));
}

/**
* Return current zoom level. Is used by integration tests to verify zoom buttons work.
* @returns {number} current zoom level
*/
getZoom() {
if (this.#map) {
return this.#map.getView().getZoom()

Check notice on line 437 in Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js#L437

Missing semicolon.
}
return -1;
}

/**
* Returns true if the canvas is currently animated. Is used by integration tests to wait for animation end.
* @returns {boolean} whether animation is still ongoing
*/
getAnimating() {
if (this.#map) {
return this.#map.getView().getAnimating();
}
return false;
}

/**
* Return current rotation. Is used by integration tests to verify rotation buttons work.
* @returns {number} current rotation
*/
getRotation() {
if (this.#map) {
return this.#map.getView().getRotation()

Check notice on line 459 in Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

Kitodo/src/main/webapp/WEB-INF/resources/js/ol_custom.js#L459

Missing semicolon.
}
return 0;
}

/**
* Is called when a resize event has happened. Unless debounced, this event is triggered potentially
* at 60fps.
Expand Down
262 changes: 262 additions & 0 deletions Kitodo/src/test/java/org/kitodo/selenium/MetadataImagePreviewST.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/*
* (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/

package org.kitodo.selenium;

import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.kitodo.MockDatabase;
import org.kitodo.data.database.beans.User;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.data.elasticsearch.exceptions.CustomResponseException;
import org.kitodo.data.exceptions.DataException;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.services.data.ProcessService;
import org.kitodo.selenium.testframework.BaseTestSelenium;
import org.kitodo.selenium.testframework.Browser;
import org.kitodo.selenium.testframework.Pages;
import org.kitodo.test.utils.ProcessTestUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

/**
* Tests the image preview panel (OpenLayers map) in the metadata editor.
*/
public class MetadataImagePreviewST extends BaseTestSelenium {

private static final String TEST_RENAME_MEDIA_FILE = "testRenameMediaMeta.xml";
private static final String PROCESS_TITLE = "Detail View";
private static final String OPEN_LAYERS_CANVAS_SELECTOR = "#imagePreviewForm .ol-layer canvas";
private static final String OPEN_LAYERS_ZOOM_IN_SELECTOR = "#imagePreviewForm .ol-zoom-in";
private static final String OPEN_LAYERS_ZOOM_OUT_SELECTOR = "#imagePreviewForm .ol-zoom-out";
private static final String OPEN_LAYERS_ZOOM_RESET_SELECTOR = "#imagePreviewForm .reset-zoom button";
private static final String OPEN_LAYERS_ROTATE_LEFT_SELECTOR = "#imagePreviewForm .rotate-left button";
private static final String OPEN_LAYERS_ROTATE_RIGHT_SELECTOR = "#imagePreviewForm .rotate-right button";
private static final String OPEN_LAYERS_ROTATE_NORTH_SELECTOR = "#imagePreviewForm .rotate-north button";
private static final String GALLERY_HEADING_WRAPPER_SELECTOR = "#galleryHeadingWrapper span";
private static final String SECOND_THUMBNAIL_SELECTOR =
"#imagePreviewForm\\:thumbnailWrapper > div:nth-child(2) .thumbnail-container";
private static final Double EPSILON = 0.001;

private static int processId = -1;

/**
* Prepare tests by inserting dummy processes into database and index for sub-folders of test metadata resources.
* @throws DAOException when saving of dummy or test processes fails.
* @throws DataException when retrieving test project for test processes fails.
* @throws IOException when copying test metadata or image files fails.
*/
@BeforeAll
public static void prepare() throws DAOException, DataException, IOException {
MockDatabase.insertFoldersForSecondProject();
processId = MockDatabase.insertTestProcessIntoSecondProject(PROCESS_TITLE);
ProcessTestUtils.copyTestFiles(processId, TEST_RENAME_MEDIA_FILE);
}

/**
* Tests whether the image preview is shown when a user clicks on the image preview button.
* @throws Exception when something fails
*/
@Test
public void imageVisibleTest() throws Exception {
login("kowal");
Pages.getProcessesPage().goTo().editMetadata(PROCESS_TITLE);

// check detail view is not yet visible
assertEquals(0, findElementsByCSS(OPEN_LAYERS_CANVAS_SELECTOR).size());

// open detail view
Pages.getMetadataEditorPage().openDetailView();

// check it is visible now
pollAssertTrue(() -> findElementsByCSS(OPEN_LAYERS_CANVAS_SELECTOR).get(0).isDisplayed());
}

/**
* Tests whether the zoom buttons of the image preview (zoom in, out, reset) work as intended.
* @throws Exception when something fails
*/
@Test
public void zoomLevelTest() throws Exception {
login("kowal");

// open detail view and wait for openlayers canvas
Pages.getProcessesPage().goTo().editMetadata(PROCESS_TITLE);
Pages.getMetadataEditorPage().openDetailView();
pollAssertTrue(() -> findElementsByCSS(OPEN_LAYERS_CANVAS_SELECTOR).get(0).isDisplayed());

// remember initial zoom
Double initialZoom = getOpenLayersZoom();
assertTrue(initialZoom > 0);

// zoom in, and check zoom increases
findElementsByCSS(OPEN_LAYERS_ZOOM_IN_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());
assertTrue(() -> getOpenLayersZoom() > initialZoom);

// zoom out, and check zoom returns to initial zoom level
findElementsByCSS(OPEN_LAYERS_ZOOM_OUT_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());
assertTrue(() -> Math.abs(getOpenLayersZoom() - initialZoom) < EPSILON);

// zoom in, and reset zoom, check zoom returns to initial zoom level
findElementsByCSS(OPEN_LAYERS_ZOOM_IN_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());
findElementsByCSS(OPEN_LAYERS_ZOOM_RESET_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());
assertTrue(() -> Math.abs(getOpenLayersZoom() - initialZoom) < EPSILON);
}

/**
* Tests whether the rotation buttons of the image preview (rotation left, right, north) work as intended.
* @throws Exception when something fails
*/
@Test
public void rotationTest() throws Exception {
login("kowal");

// open detail view and wait for openlayers canvas
Pages.getProcessesPage().goTo().editMetadata(PROCESS_TITLE);
Pages.getMetadataEditorPage().openDetailView();
pollAssertTrue(() -> findElementsByCSS(OPEN_LAYERS_CANVAS_SELECTOR).get(0).isDisplayed());

// check initial rotation is zero
assertTrue(Math.abs(getOpenLayersRotation()) < EPSILON);

// rotate left and check rotation is decreasing
findElementsByCSS(OPEN_LAYERS_ROTATE_LEFT_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());
assertTrue(() -> getOpenLayersRotation() < 0.0);

// rotate back, and check rotation returns to zero
findElementsByCSS(OPEN_LAYERS_ROTATE_RIGHT_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());
assertTrue(() -> Math.abs(getOpenLayersRotation()) < EPSILON);

// rotate left and reset to north, check rotation returns to zero
findElementsByCSS(OPEN_LAYERS_ROTATE_LEFT_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());
findElementsByCSS(OPEN_LAYERS_ROTATE_NORTH_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());
assertTrue(() -> Math.abs(getOpenLayersRotation()) < EPSILON);
}

/**
* Tests that both zoom level and rotation persists when a user clicks on another image
* (which causes OpenLayers to be loaded again).
* @throws Exception when something fails
*/
@Test
public void viewPersistsImageChange() throws Exception {
login("kowal");

// open detail view and wait for openlayers canvas
Pages.getProcessesPage().goTo().editMetadata(PROCESS_TITLE);
Pages.getMetadataEditorPage().openDetailView();
pollAssertTrue(() -> findElementsByCSS(OPEN_LAYERS_CANVAS_SELECTOR).get(0).isDisplayed());

// remember initial zoom, rotation
Double initialZoom = getOpenLayersZoom();
Double initialRotation = getOpenLayersRotation();

// rotate left and zoom in
findElementsByCSS(OPEN_LAYERS_ROTATE_LEFT_SELECTOR).get(0).click();
findElementsByCSS(OPEN_LAYERS_ZOOM_IN_SELECTOR).get(0).click();
pollAssertTrue(() -> !isOpenLayersAnimating());

// remember changed zoom, rotation
Double changedZoom = getOpenLayersZoom();
Double changedRotation = getOpenLayersRotation();

// verify zoom and rotation was applied
assertTrue(Math.abs(initialZoom - changedZoom) > 0);
assertTrue(Math.abs(initialRotation - changedRotation) > 0);

// change to second image
findElementsByCSS(SECOND_THUMBNAIL_SELECTOR).get(0).click();

// wait until second image has been loaded
pollAssertTrue(
() -> "Bild 1, Seite -".equals(
findElementsByCSS(GALLERY_HEADING_WRAPPER_SELECTOR).get(0).getText().strip()
)
);

// wait until OpenLayers canvas is available
pollAssertTrue(() -> findElementsByCSS(OPEN_LAYERS_CANVAS_SELECTOR).get(0).isDisplayed());

// check that rotation and zoom was correctly applied to next image (and is not reset)
assertTrue(Math.abs(getOpenLayersZoom() - changedZoom) < EPSILON);
assertTrue(Math.abs(getOpenLayersRotation() - changedRotation) < EPSILON);
}

/**
* Close metadata editor and logout after every test.
* @throws Exception when page navigation fails
*/
@AfterEach
public void closeEditorAndLogout() throws Exception {
Pages.getMetadataEditorPage().closeEditor();
Pages.getTopNavigation().logout();
}

/**
* Cleanup test environment by removing temporal dummy processes from database and index.
* @throws DAOException when dummy process cannot be removed from database
* @throws CustomResponseException when dummy process cannot be removed from index
* @throws DataException when dummy process cannot be removed from index
* @throws IOException when deleting test files fails.
*/
@AfterAll
public static void cleanup() throws DAOException, CustomResponseException, DataException, IOException {
ProcessService.deleteProcess(processId);
}

private void login(String username) throws InstantiationException, IllegalAccessException, InterruptedException {
User metadataUser = ServiceManager.getUserService().getByLogin(username);
Pages.getLoginPage().goTo().performLogin(metadataUser);
}

private List<WebElement> findElementsByCSS(String css) {
return Browser.getDriver().findElements(By.cssSelector(css));
}

private void pollAssertTrue(Callable<Boolean> conditionEvaluator) throws Exception {
await().ignoreExceptions().pollInterval(100, TimeUnit.MILLISECONDS).atMost(3, TimeUnit.SECONDS)
.until(conditionEvaluator);
}

private Boolean isOpenLayersAnimating() {
return (Boolean)Browser.getDriver().executeScript("return metadataEditor.detailMap.getAnimating()");
}

private Double getOpenLayersZoom() {
Object result = Browser.getDriver().executeScript("return metadataEditor.detailMap.getZoom()");
return ((Number)result).doubleValue();
}

private Double getOpenLayersRotation() {
Object result = Browser.getDriver().executeScript("return metadataEditor.detailMap.getRotation()");
return ((Number)result).doubleValue();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ public class MetadataEditorPage extends Page<MetadataEditorPage> {
@FindBy(id = "renamingMediaResultForm:okSuccess")
private WebElement okButtonRenameMediaFiles;

@FindBy(id = "imagePreviewForm:previewButton")
private WebElement imagePreviewButton;

public MetadataEditorPage() {
super("metadataEditor.jsf");
}
Expand Down Expand Up @@ -222,4 +225,12 @@ public long getNumberOfDisplayedStructureElements() {
return Browser.getDriver().findElements(By.cssSelector(".ui-treenode")).stream().filter(WebElement::isDisplayed)
.count();
}

/**
* Open detail view by clicking on image preview button.
*/
public void openDetailView() {
imagePreviewButton.click();
}

}

0 comments on commit 0852f11

Please sign in to comment.