diff --git a/docs/screenshots/01_hub.png b/docs/screenshots/01_hub.png index 348a1de..00d4e9b 100644 Binary files a/docs/screenshots/01_hub.png and b/docs/screenshots/01_hub.png differ diff --git a/docs/screenshots/01_hub_1024x768.png b/docs/screenshots/01_hub_1024x768.png new file mode 100644 index 0000000..00d4e9b Binary files /dev/null and b/docs/screenshots/01_hub_1024x768.png differ diff --git a/docs/screenshots/01_hub_780x500.png b/docs/screenshots/01_hub_780x500.png new file mode 100644 index 0000000..00d4e9b Binary files /dev/null and b/docs/screenshots/01_hub_780x500.png differ diff --git a/docs/screenshots/01_hub_claro.png b/docs/screenshots/01_hub_claro.png index 348a1de..00d4e9b 100644 Binary files a/docs/screenshots/01_hub_claro.png and b/docs/screenshots/01_hub_claro.png differ diff --git a/docs/screenshots/01_hub_scale150.png b/docs/screenshots/01_hub_scale150.png new file mode 100644 index 0000000..7977e1f Binary files /dev/null and b/docs/screenshots/01_hub_scale150.png differ diff --git a/docs/screenshots/01_layout_claro.png b/docs/screenshots/01_layout_claro.png index 348a1de..00d4e9b 100644 Binary files a/docs/screenshots/01_layout_claro.png and b/docs/screenshots/01_layout_claro.png differ diff --git a/docs/screenshots/02_diagnostico.png b/docs/screenshots/02_diagnostico.png index 38c1c42..b4f04db 100644 Binary files a/docs/screenshots/02_diagnostico.png and b/docs/screenshots/02_diagnostico.png differ diff --git a/docs/screenshots/02_diagnostico_claro.png b/docs/screenshots/02_diagnostico_claro.png index 38c1c42..b4f04db 100644 Binary files a/docs/screenshots/02_diagnostico_claro.png and b/docs/screenshots/02_diagnostico_claro.png differ diff --git a/docs/screenshots/03_configuracoes.png b/docs/screenshots/03_configuracoes.png index c54ee34..c274ba5 100644 Binary files a/docs/screenshots/03_configuracoes.png and b/docs/screenshots/03_configuracoes.png differ diff --git a/docs/screenshots/03_configuracoes_claro.png b/docs/screenshots/03_configuracoes_claro.png index c54ee34..c274ba5 100644 Binary files a/docs/screenshots/03_configuracoes_claro.png and b/docs/screenshots/03_configuracoes_claro.png differ diff --git a/docs/screenshots/04_certificados.png b/docs/screenshots/04_certificados.png index 8584718..66c7db4 100644 Binary files a/docs/screenshots/04_certificados.png and b/docs/screenshots/04_certificados.png differ diff --git a/docs/screenshots/04_certificados_claro.png b/docs/screenshots/04_certificados_claro.png index 8584718..66c7db4 100644 Binary files a/docs/screenshots/04_certificados_claro.png and b/docs/screenshots/04_certificados_claro.png differ diff --git a/docs/screenshots/05_validacao.png b/docs/screenshots/05_validacao.png index 9e704b1..4eb6327 100644 Binary files a/docs/screenshots/05_validacao.png and b/docs/screenshots/05_validacao.png differ diff --git a/docs/screenshots/05_validacao_claro.png b/docs/screenshots/05_validacao_claro.png index 9e704b1..4eb6327 100644 Binary files a/docs/screenshots/05_validacao_claro.png and b/docs/screenshots/05_validacao_claro.png differ diff --git a/docs/screenshots/06_solicitacao.png b/docs/screenshots/06_solicitacao.png index 38ae7a0..88989c8 100644 Binary files a/docs/screenshots/06_solicitacao.png and b/docs/screenshots/06_solicitacao.png differ diff --git a/docs/screenshots/06_solicitacao_claro.png b/docs/screenshots/06_solicitacao_claro.png index 38ae7a0..88989c8 100644 Binary files a/docs/screenshots/06_solicitacao_claro.png and b/docs/screenshots/06_solicitacao_claro.png differ diff --git a/docs/screenshots/07_selos.png b/docs/screenshots/07_selos.png index 4172466..78ed621 100644 Binary files a/docs/screenshots/07_selos.png and b/docs/screenshots/07_selos.png differ diff --git a/docs/screenshots/07_selos_claro.png b/docs/screenshots/07_selos_claro.png index 4172466..78ed621 100644 Binary files a/docs/screenshots/07_selos_claro.png and b/docs/screenshots/07_selos_claro.png differ diff --git a/docs/screenshots/08_sobre.png b/docs/screenshots/08_sobre.png index a331a33..6098db4 100644 Binary files a/docs/screenshots/08_sobre.png and b/docs/screenshots/08_sobre.png differ diff --git a/docs/screenshots/08_sobre_claro.png b/docs/screenshots/08_sobre_claro.png index a331a33..6098db4 100644 Binary files a/docs/screenshots/08_sobre_claro.png and b/docs/screenshots/08_sobre_claro.png differ diff --git a/docs/screenshots/09_exit_dialog.png b/docs/screenshots/09_exit_dialog.png index d60c35c..c4be299 100644 Binary files a/docs/screenshots/09_exit_dialog.png and b/docs/screenshots/09_exit_dialog.png differ diff --git a/docs/screenshots/09_exit_dialog_claro.png b/docs/screenshots/09_exit_dialog_claro.png index d60c35c..c4be299 100644 Binary files a/docs/screenshots/09_exit_dialog_claro.png and b/docs/screenshots/09_exit_dialog_claro.png differ diff --git a/docs/screenshots/10_pdf_sign.png b/docs/screenshots/10_pdf_sign.png index 568bfa1..0547e88 100644 Binary files a/docs/screenshots/10_pdf_sign.png and b/docs/screenshots/10_pdf_sign.png differ diff --git a/docs/screenshots/10_pdf_sign_claro.png b/docs/screenshots/10_pdf_sign_claro.png index 568bfa1..0547e88 100644 Binary files a/docs/screenshots/10_pdf_sign_claro.png and b/docs/screenshots/10_pdf_sign_claro.png differ diff --git a/docs/screenshots/11_pdf_cert_sign.png b/docs/screenshots/11_pdf_cert_sign.png index a2e5def..2b6e19e 100644 Binary files a/docs/screenshots/11_pdf_cert_sign.png and b/docs/screenshots/11_pdf_cert_sign.png differ diff --git a/docs/screenshots/11_pdf_cert_sign_claro.png b/docs/screenshots/11_pdf_cert_sign_claro.png index a2e5def..2b6e19e 100644 Binary files a/docs/screenshots/11_pdf_cert_sign_claro.png and b/docs/screenshots/11_pdf_cert_sign_claro.png differ diff --git a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/UiPreviewGenerator.java b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/UiPreviewGenerator.java index 765bf1d..f4d7531 100644 --- a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/UiPreviewGenerator.java +++ b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/UiPreviewGenerator.java @@ -28,7 +28,9 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.Scene; +import javafx.scene.SnapshotParameters; import javafx.scene.control.Button; +import javafx.scene.transform.Transform; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.image.PixelReader; @@ -108,13 +110,33 @@ public class UiPreviewGenerator extends Application { private void captureHubScreenshots(Stage stage) throws Exception { MainAppView appView = new MainAppView(stage); + + // 1. Resolução padrão 780x500 + stage.setWidth(780); + stage.setHeight(500); stage.show(); stage.getScene().getRoot().applyCss(); stage.getScene().getRoot().layout(); saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub.png")); + saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub_780x500.png")); saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub_claro.png")); saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_layout_claro.png")); + + // 2. Escala 150% (High-DPI / Zoom 1.5x) + saveNodeSnapshotScaled(stage.getScene(), new File(OUTPUT_DIR, "01_hub_scale150.png"), 1.5); + + // 3. Resolução expandida 1024x768 + stage.setWidth(1024); + stage.setHeight(768); + stage.getScene().getRoot().applyCss(); + stage.getScene().getRoot().layout(); + + saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub_1024x768.png")); + + // Restaura tamanho padrão para os diálogos subsequentes + stage.setWidth(780); + stage.setHeight(500); } private void captureDiagnosticsScreenshots(Stage stage) throws Exception { @@ -551,7 +573,15 @@ public class UiPreviewGenerator extends Application { } private static void saveNodeSnapshot(Scene scene, File targetFile) throws Exception { - WritableImage image = scene.snapshot(null); + saveNodeSnapshotScaled(scene, targetFile, 1.0); + } + + private static void saveNodeSnapshotScaled(Scene scene, File targetFile, double scale) throws Exception { + SnapshotParameters params = new SnapshotParameters(); + if (scale != 1.0) { + params.setTransform(Transform.scale(scale, scale)); + } + WritableImage image = scene.getRoot().snapshot(params, null); int width = (int) image.getWidth(); int height = (int) image.getHeight(); BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); @@ -562,6 +592,6 @@ public class UiPreviewGenerator extends Application { } } ImageIO.write(bufferedImage, "png", targetFile); - System.out.println("Screenshot gerada: " + targetFile.getName()); + System.out.println("Screenshot gerada: " + targetFile.getName() + " (" + width + "x" + height + ")"); } } diff --git a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/view/ButtonLayoutIntegrityTest.java b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/view/ButtonLayoutIntegrityTest.java new file mode 100644 index 0000000..3cb46db --- /dev/null +++ b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/view/ButtonLayoutIntegrityTest.java @@ -0,0 +1,406 @@ +package br.com.cybercore.signer.ui.view; + +import br.com.cybercore.signer.core.crypto.SigningService; +import br.com.cybercore.signer.core.domain.CertificateStatus; +import br.com.cybercore.signer.core.domain.CertificateType; +import br.com.cybercore.signer.core.domain.ManagedCertificate; +import br.com.cybercore.signer.core.domain.SigningContext; +import br.com.cybercore.signer.core.pdf.PdfSigningService; +import br.com.cybercore.signer.core.pdf.seal.CpfDisplayMode; +import br.com.cybercore.signer.core.pdf.seal.PdfSealThemeRegistry; +import br.com.cybercore.signer.core.provider.CertificateManager; +import br.com.cybercore.signer.ui.components.SvgIcons; +import javafx.application.Platform; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.stage.Stage; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Teste automatizado de integridade de layout para telas e diálogos do CyberCore Signer. + * Garante que nenhum botão tenha seu texto ou área comprimida/truncada (largura ou altura menor que pref). + * Valida também as dimensões mínimas dos cartões do Hub (min 68px) e da lista lateral (min 40px). + */ +class ButtonLayoutIntegrityTest { + + private static boolean javaFxAvailable = false; + + @BeforeAll + static void initJavaFx() { + CountDownLatch latch = new CountDownLatch(1); + try { + Platform.startup(() -> { + javaFxAvailable = true; + latch.countDown(); + }); + if (!latch.await(5, TimeUnit.SECONDS)) { + javaFxAvailable = false; + } + } catch (Throwable t) { + if (t instanceof IllegalStateException && t.getMessage() != null + && t.getMessage().contains("Toolkit already initialized")) { + javaFxAvailable = true; + } else { + javaFxAvailable = false; + } + } + } + + private void runOnFxThread(ThrowingRunnable action) throws Throwable { + org.junit.jupiter.api.Assumptions.assumeTrue(javaFxAvailable, "Ambiente gráfico JavaFX (X11/Xvfb) não disponível."); + + CountDownLatch latch = new CountDownLatch(1); + AtomicReference error = new AtomicReference<>(); + + Platform.runLater(() -> { + try { + action.run(); + } catch (Throwable t) { + error.set(t); + } finally { + latch.countDown(); + } + }); + + assertThat(latch.await(8, TimeUnit.SECONDS)).isTrue(); + if (error.get() != null) { + throw error.get(); + } + } + + @FunctionalInterface + interface ThrowingRunnable { + void run() throws Throwable; + } + + private void layoutAndAssert(Stage stage, double width, double height, String screenName) { + Scene scene = stage.getScene(); + if (width > 0 && height > 0) { + stage.setWidth(width); + stage.setHeight(height); + } + Parent root = scene.getRoot(); + root.applyCss(); + if (width > 0 && height > 0) { + root.resizeRelocate(0, 0, width, height); + } + root.layout(); + + List