blob: 340350dcd62158eb93f989ff44a5bfa227176826 [file]
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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.
package org.openqa.selenium;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assumptions.assumeThat;
import static org.openqa.selenium.support.ui.ExpectedConditions.frameToBeAvailableAndSwitchToIt;
import static org.openqa.selenium.support.ui.ExpectedConditions.titleIs;
import static org.openqa.selenium.support.ui.ExpectedConditions.visibilityOfAllElementsLocatedBy;
import static org.openqa.selenium.testing.drivers.Browser.CHROME;
import static org.openqa.selenium.testing.drivers.Browser.EDGE;
import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import javax.imageio.ImageIO;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.testing.Ignore;
import org.openqa.selenium.testing.JupiterTestBase;
import org.openqa.selenium.testing.SwitchToTopAfterTest;
/**
* Test screenshot feature.
*
* <p>1. check output for all possible types
*
* <p>2. check screenshot image
*
* <p>Logic of screenshot check test is simple: - open page with fixed amount of fixed sized and
* coloured areas - take screenshot - calculate expected colors as in tested HTML page - scan
* screenshot for actual colors * compare
*/
// TODO(user): verify expected behaviour after frame switching
// TODO(user): test screenshots at guaranteed maximized browsers
// TODO(user): test screenshots at guaranteed non maximized browsers
// TODO(user): test screenshots at guaranteed minimized browsers
// TODO(user): test screenshots at guaranteed fullscreened/kiosked browsers (WINDOWS platform
// specific)
class TakesScreenshotTest extends JupiterTestBase {
private TakesScreenshot screenshooter;
private File tempFile = null;
@BeforeEach
public void setUp() {
assumeThat(driver).isInstanceOf(TakesScreenshot.class);
screenshooter = (TakesScreenshot) driver;
}
@AfterEach
public void tearDown() {
if (tempFile != null) {
if (!tempFile.delete()) {
System.err.println("Unable to delete temp file");
}
tempFile = null;
}
}
@Test
void testGetScreenshotAsFile() {
driver.get(pages.simpleTestPage);
tempFile = screenshooter.getScreenshotAs(OutputType.FILE);
assertThat(tempFile).exists().isNotEmpty();
}
@Test
void testGetScreenshotAsBase64() {
driver.get(pages.simpleTestPage);
String screenshot = screenshooter.getScreenshotAs(OutputType.BASE64);
assertThat(screenshot.length()).isPositive();
}
@Test
void testGetScreenshotAsBinary() {
driver.get(pages.simpleTestPage);
byte[] screenshot = screenshooter.getScreenshotAs(OutputType.BYTES);
assertThat(screenshot.length).isPositive();
}
@Test
@Ignore(value = CHROME, gitHubActions = true)
public void testShouldCaptureScreenshotOfCurrentViewport() throws IOException {
driver.get(appServer.whereIs("screen/screen.html"));
BufferedImage screenshot = getImage();
Set<String> actualColors =
scanActualColors(screenshot, /* stepX in pixels */ 5, /* stepY in pixels */ 5);
Set<String> expectedColors =
generateExpectedColors(
/* initial color */ 0x0F0F0F,
/* color step */ 1000,
/* grid X size */ 6,
/* grid Y size */ 6);
compareColors(expectedColors, actualColors);
}
@Ignore(value = CHROME, gitHubActions = true)
@Test
void testShouldCaptureScreenshotOfAnElement() throws Exception {
driver.get(appServer.whereIs("screen/screen.html"));
WebElement element = driver.findElement(By.id("cell11"));
byte[] imageData = element.getScreenshotAs(OutputType.BYTES);
assertThat(imageData).isNotNull().isNotEmpty();
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));
assertThat(image).isNotNull();
Raster raster = image.getRaster();
String hex =
String.format(
"#%02x%02x%02x",
(raster.getSample(1, 1, 0)), (raster.getSample(1, 1, 1)), (raster.getSample(1, 1, 2)));
assertThat(hex).isEqualTo("#0f12f7");
}
@Test
@Ignore(value = CHROME, gitHubActions = true)
public void testShouldCaptureScreenshotAtFramePage() throws IOException {
driver.get(appServer.whereIs("screen/screen_frames.html"));
wait.until(frameToBeAvailableAndSwitchToIt(By.id("frame1")));
wait.until(visibilityOfAllElementsLocatedBy(By.id("content")));
driver.switchTo().defaultContent();
wait.until(frameToBeAvailableAndSwitchToIt(By.id("frame2")));
wait.until(visibilityOfAllElementsLocatedBy(By.id("content")));
driver.switchTo().defaultContent();
wait.until(titleIs("screen test"));
BufferedImage screenshot = getImage();
Set<String> actualColors =
scanActualColors(screenshot, /* stepX in pixels */ 5, /* stepY in pixels */ 5);
Set<String> expectedColors = new HashSet<>();
expectedColors.addAll(
generateExpectedColors(
/* initial color */ 0x0F0F0F,
/* color step*/ 1000,
/* grid X size */ 6,
/* grid Y size */ 6));
expectedColors.addAll(
generateExpectedColors(
/* initial color */ 0xDFDFDF,
/* color step*/ 1000,
/* grid X size */ 6,
/* grid Y size */ 6));
// expectation is that screenshot at page with frames will be taken for full page
compareColors(expectedColors, actualColors);
}
@Test
@Ignore(CHROME)
@Ignore(EDGE)
public void testShouldCaptureScreenshotAtIFramePage() throws IOException {
driver.get(appServer.whereIs("screen/screen_iframes.html"));
BufferedImage screenshot = getImage();
Set<String> actualColors =
scanActualColors(screenshot, /* stepX in pixels */ 5, /* stepY in pixels */ 5);
Set<String> expectedColors = new HashSet<>();
expectedColors.addAll(
generateExpectedColors(
/* initial color */ 0x0F0F0F,
/* color step*/ 1000,
/* grid X size */ 6,
/* grid Y size */ 6));
expectedColors.addAll(
generateExpectedColors(
/* initial color */ 0xDFDFDF,
/* color step*/ 1000,
/* grid X size */ 6,
/* grid Y size */ 6));
// expectation is that screenshot at page with Iframes will be taken for full page
compareColors(expectedColors, actualColors);
}
@SwitchToTopAfterTest
@Test
@Ignore(FIREFOX)
@Ignore(value = CHROME, gitHubActions = true)
public void testShouldCaptureScreenshotAtFramePageAfterSwitching() throws IOException {
driver.get(appServer.whereIs("screen/screen_frames.html"));
driver.switchTo().frame(driver.findElement(By.id("frame2")));
BufferedImage screenshot = getImage();
Set<String> actualColors =
scanActualColors(screenshot, /* stepX in pixels */ 5, /* stepY in pixels */ 5);
Set<String> expectedColors = new HashSet<>();
expectedColors.addAll(
generateExpectedColors(
/* initial color */ 0x0F0F0F,
/* color step*/ 1000,
/* grid X size */ 6,
/* grid Y size */ 6));
expectedColors.addAll(
generateExpectedColors(
/* initial color */ 0xDFDFDF,
/* color step*/ 1000,
/* grid X size */ 6,
/* grid Y size */ 6));
// expectation is that screenshot at page with frames after switching to a frame
// will be taken for full page
compareColors(expectedColors, actualColors);
}
@SwitchToTopAfterTest
@Test
@Ignore(CHROME)
@Ignore(EDGE)
@Ignore(FIREFOX)
public void testShouldCaptureScreenshotAtIFramePageAfterSwitching() throws IOException {
driver.get(appServer.whereIs("screen/screen_iframes.html"));
driver.switchTo().frame(driver.findElement(By.id("iframe2")));
BufferedImage screenshot = getImage();
Set<String> actualColors =
scanActualColors(screenshot, /* stepX in pixels */ 5, /* stepY in pixels */ 5);
Set<String> expectedColors = new HashSet<>();
expectedColors.addAll(
generateExpectedColors(
/* initial color */ 0x0F0F0F,
/* color step*/ 1000,
/* grid X size */ 6,
/* grid Y size */ 6));
expectedColors.addAll(
generateExpectedColors(
/* initial color */ 0xDFDFDF,
/* color step*/ 1000,
/* grid X size */ 6,
/* grid Y size */ 6));
// expectation is that screenshot at page with Iframes after switching to iframe
// will be taken for full page
compareColors(expectedColors, actualColors);
}
/**
* get actual image screenshot
*
* @return Image object
*/
private BufferedImage getImage() throws IOException {
byte[] imageData = screenshooter.getScreenshotAs(OutputType.BYTES);
assertThat(imageData).isNotNull();
assertThat(imageData.length).isPositive();
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));
assertThat(image).isNotNull();
// saveImageToTmpFile(image);
return image;
}
/**
* generate expected colors as in checked page.
*
* @param initialColor - initial color of first (right top) cell of grid
* @param stepColor - step b/w grid colors as number
* @param nX - grid size at X dimension
* @param nY - grid size at Y dimension
* @return set of colors in string hex presentation
*/
private Set<String> generateExpectedColors(
final int initialColor, final int stepColor, final int nX, final int nY) {
Set<String> colors = new TreeSet<>();
int cnt = 1;
for (int i = 1; i < nX; i++) {
for (int j = 1; j < nY; j++) {
int color = initialColor + (cnt * stepColor);
String hex =
String.format(
"#%02x%02x%02x",
((color & 0xFF0000) >> 16), ((color & 0x00FF00) >> 8), ((color & 0x0000FF)));
colors.add(hex);
cnt++;
}
}
return colors;
}
/**
* Get colors from image from each point at grid defined by stepX/stepY.
*
* @param image - image
* @param stepX - interval in pixels b/w point in X dimension
* @param stepY - interval in pixels b/w point in Y dimension
* @return set of colors in string hex presentation
*/
private Set<String> scanActualColors(BufferedImage image, final int stepX, final int stepY) {
Set<String> colors = new TreeSet<>();
int height = image.getHeight();
int width = image.getWidth();
assertThat(width).isPositive();
assertThat(height).isPositive();
Raster raster = image.getRaster();
for (int i = 0; i < width; i = i + stepX) {
for (int j = 0; j < height; j = j + stepY) {
String hex =
String.format(
"#%02x%02x%02x",
(raster.getSample(i, j, 0)),
(raster.getSample(i, j, 1)),
(raster.getSample(i, j, 2)));
colors.add(hex);
}
}
assertThat(colors).isNotEmpty();
return colors;
}
/**
* Compares sets of colors are same.
*
* @param expectedColors - set of expected colors
* @param actualColors - set of actual colors
*/
private void compareColors(Set<String> expectedColors, Set<String> actualColors) {
assertThat(onlyBlack(actualColors)).as("Only black").isFalse();
assertThat(onlyWhite(actualColors)).as("Only white").isFalse();
// Ignore black and white for further comparison
Set<String> cleanActualColors = new HashSet<>(actualColors);
cleanActualColors.remove("#000000");
cleanActualColors.remove("#ffffff");
assertThat(cleanActualColors).containsExactlyInAnyOrderElementsOf(expectedColors);
}
private boolean onlyBlack(Set<String> colors) {
return colors.size() == 1 && "#000000".equals(colors.toArray()[0]);
}
private boolean onlyWhite(Set<String> colors) {
return colors.size() == 1 && "#ffffff".equals(colors.toArray()[0]);
}
/**
* Simple helper to save screenshot to tmp file. For debug purposes.
*
* @param im image
*/
@SuppressWarnings("unused")
private void saveImageToTmpFile(String testMethodName, BufferedImage im) throws IOException {
File outputfile = new File(testMethodName + "_image.png");
ImageIO.write(im, "png", outputfile);
}
}