test(ui): add design token guardrail tests
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
|||||||
|
package br.com.cybercore.signer.ui.theme;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testes de guarda-corpo arquitetural para o sistema de Design Tokens do CyberCore Signer.
|
||||||
|
* Assegura que nenhuma cor hexadecimal solta seja introduzida fora dos blocos canônicos de tokens,
|
||||||
|
* garantindo conformidade com o tema escuro/claro e consistência visual rigorosa.
|
||||||
|
*/
|
||||||
|
class DesignTokenGuardrailTest {
|
||||||
|
|
||||||
|
private static final Pattern HEX_COLOR_PATTERN = Pattern.compile("#([0-9a-fA-F]{3,8})\\b");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("theme.css não deve conter cores hexadecimais fora dos blocos de tokens (.theme-light e .theme-dark)")
|
||||||
|
void shouldNotHaveHardcodedHexColorsOutsideTokenDefinitions() throws Exception {
|
||||||
|
InputStream stream = getClass().getResourceAsStream("/styles/theme.css");
|
||||||
|
assertThat(stream).withFailMessage("Arquivo /styles/theme.css não encontrado no classpath!").isNotNull();
|
||||||
|
|
||||||
|
List<String> violations = new ArrayList<>();
|
||||||
|
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
int lineNumber = 0;
|
||||||
|
boolean insideTokenBlock = false;
|
||||||
|
int openBracesInBlock = 0;
|
||||||
|
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
lineNumber++;
|
||||||
|
String trimmed = line.trim();
|
||||||
|
|
||||||
|
// Ignora comentários de linha inteira
|
||||||
|
if (trimmed.startsWith("/*") && trimmed.endsWith("*/")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detecta entrada nos blocos de tokens .theme-light e .theme-dark
|
||||||
|
if (trimmed.contains(".theme-light") || trimmed.contains(".theme-dark")) {
|
||||||
|
insideTokenBlock = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insideTokenBlock) {
|
||||||
|
for (char c : line.toCharArray()) {
|
||||||
|
if (c == '{') {
|
||||||
|
openBracesInBlock++;
|
||||||
|
} else if (c == '}') {
|
||||||
|
openBracesInBlock--;
|
||||||
|
if (openBracesInBlock <= 0) {
|
||||||
|
insideTokenBlock = false;
|
||||||
|
openBracesInBlock = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue; // Cores hex dentro do bloco de tokens são permitidas
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fora dos blocos de tokens: proibir qualquer cor hexadecimal
|
||||||
|
// Remove comentários inline antes de testar
|
||||||
|
String codeOnly = line.replaceAll("/\\*.*?\\*/", "");
|
||||||
|
Matcher matcher = HEX_COLOR_PATTERN.matcher(codeOnly);
|
||||||
|
while (matcher.find()) {
|
||||||
|
violations.add(String.format("Linha %d: '%s' contém cor hexadecimal solta '%s'",
|
||||||
|
lineNumber, trimmed, matcher.group()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(violations)
|
||||||
|
.withFailMessage("Foram encontradas cores hexadecimais soltas fora dos blocos de tokens em theme.css:\n%s",
|
||||||
|
String.join("\n", violations))
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("theme.css não deve conter seletores legados residuais serpro-*")
|
||||||
|
void shouldNotContainLegacySerproSelectors() throws Exception {
|
||||||
|
InputStream stream = getClass().getResourceAsStream("/styles/theme.css");
|
||||||
|
assertThat(stream).isNotNull();
|
||||||
|
|
||||||
|
List<String> legacySelectors = new ArrayList<>();
|
||||||
|
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
|
||||||
|
String line;
|
||||||
|
int lineNumber = 0;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
lineNumber++;
|
||||||
|
if (line.toLowerCase().contains(".serpro-")) {
|
||||||
|
legacySelectors.add(String.format("Linha %d: %s", lineNumber, line.trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(legacySelectors)
|
||||||
|
.withFailMessage("Foram encontrados seletores legados .serpro-* em theme.css:\n%s",
|
||||||
|
String.join("\n", legacySelectors))
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("theme.css deve definir todos os tokens de cores semânticas primários e de superfície")
|
||||||
|
void shouldDefineAllRequiredSemanticColorTokens() throws Exception {
|
||||||
|
InputStream stream = getClass().getResourceAsStream("/styles/theme.css");
|
||||||
|
assertThat(stream).isNotNull();
|
||||||
|
|
||||||
|
String content;
|
||||||
|
try (stream) {
|
||||||
|
content = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] requiredTokens = {
|
||||||
|
"-fx-color-primary",
|
||||||
|
"-fx-color-primary-hover",
|
||||||
|
"-fx-color-surface",
|
||||||
|
"-fx-color-surface-hover",
|
||||||
|
"-fx-color-background",
|
||||||
|
"-fx-color-border",
|
||||||
|
"-fx-color-text",
|
||||||
|
"-fx-color-text-muted",
|
||||||
|
"-fx-color-text-inverse",
|
||||||
|
"-fx-color-success",
|
||||||
|
"-fx-color-warning",
|
||||||
|
"-fx-color-error"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String token : requiredTokens) {
|
||||||
|
assertThat(content)
|
||||||
|
.withFailMessage("Token semântico obrigatório ausente em theme.css: %s", token)
|
||||||
|
.contains(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user