Das ist tatsächlich nicht so trivial, aber der Reihe nach:
Wer danach googelt, findet erstaunlich (?) häufig den Ansatz, die URL auf endsWith(“.pdf”) zu prüfen 😳 Das sagt natürlich nichts darüber, ob das PDF auch dargestellt wird (oder ob es das richtige PDF ist), aber als erste Näherung habe ich das übernommen. Allerdings prüfe ich die URL etwas genauer, und vergleiche sie mit der URL, deren PDF-Ausgabe backendseitig bereits inhaltlich geprüft wird:
1 2 |
String expected = getBaseUrl() + "item/[0-9]+/print" url.matches(expected) |
Kurz hatte ich dann überlegt, auf den Content Type zu prüfen, aber da kann natürlich alles drin stehen. Die o.g. Bedingungen stellt das ebenfalls nicht sicher.
Dann gibt es Ansätze, STRG+A/STRG+C zu nutzen (Beispiel), um den Inhalt des PDF zu verifizieren – funktioniert nicht mit jedem OS/Browser.
Dann, ebenfalls häufig genannt: PDFBox. Finde ich gut, so kommt man an den Inhalt des PDFs und kann sicherstellen, dass es das richtige PDF ist:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import org.apache.commons.codec.binary.Base64 import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.text.PDFTextStripper // test if there's a PDF at this URI: PDDocument pdf = getAsPDF(url) noExceptionThrown() pdf != null // and if the PDF seems to be valid: String text = extractTextFromPDF(pdf) text.contains("foo bar") // ... mit: PDDocument getAsPDF(String theUrl) throws Exception { // bonus points: The end point needs authentification :-) byte[] credentials = new Base64().encode("admin:admin".getBytes()) String basicAuth = "Basic " + new String(credentials) URL url = new URL(theUrl) URLConnection uc = url.openConnection() uc.setRequestProperty("Authorization", basicAuth) PDDocument.load(uc.getInputStream()) } String extractTextFromPDF(PDDocument pdf) { PDFTextStripper pdfStripper = new PDFTextStripper() pdfStripper.setStartPage(1) // 1-based pdfStripper.setEndPage(pdf.getPages().count) // inclusive pdfStripper.getText(pdf) } |
Nachteil: Dass die URL [ein|das richtige] PDF ausliefert, sagt natürlich nichts darüber, dass das im Browser dargestellt wird 🙃
Also: Screenshot machen. Allerdings nicht die Selenium-Methode, denn aus Browser-Sicht ist die Seite “leer”, das PDF wird von einem Plugin gerendert. Die Lösung dazu: Robot.
ACHTUNG: Das geht nicht in einer “headless”-Umgebung, wie man sie bsplw. auf vielen CI-Servern findet:
java.awt.AWTException: headless environment
Mit einem waitFor, bis das PDF tatsächlich gerendert wurde (denn das ist ungleich “die Seite wurde geladen”):
1 2 3 4 5 6 |
BufferedImage screenshot = null waitFor { screenshot = getScreenshot(driver) !new Color(screenshot.getRGB(100, 100)).equals(Color.white) } |
und einem kleinen Trick, um statt des Bildschirms nur den Browserinhalt zu screenshoten:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
static Rectangle getWindowRectangle(WebDriver driver) { WebDriver.Window window = driver.manage().window() new Rectangle( window.position.x, window.position.y, window.size.width, window.size.height ) } /** * Works with PDF etc as well. */ static BufferedImage getScreenshot(WebDriver driver) { new Robot().createScreenCapture(getWindowRectangle(driver)); } |
Auf Basis des Bildes kann man nun sicherstellen, dass keine weiße Seite gerendert wurde…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
boolean foundRefColor1 = false boolean foundRefColor2 = false for (int x = 0; x < screenshot.width; x++) { for (int y = 0; y < screenshot.height; y++) { Color current = new Color(screenshot.getRGB(x, y)) if (!foundRefColor1 && current.equals(COLOR_1)) { foundRefColor1 = true break } else if (!foundRefColor2 && current.equals(COLOR_2)) { foundRefColor2 = true break } } if (foundRefColor1 && foundRefColor2) { break } } foundRefColor1 && foundRefColor2 |
…oder halt irgendwas. Man könnte auch PNG-Vergleiche machen. Inhaltlich, wie gesagt, würde man mit PDFBox testen, darum ging es hier aber nicht.
HTH.