robotframework-quality-scanner 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner.egg-info → robotframework_quality_scanner-0.4.0}/PKG-INFO +20 -6
- {robotframework-quality-scanner-0.3.0 → robotframework_quality_scanner-0.4.0}/README.md +18 -4
- {robotframework-quality-scanner-0.3.0 → robotframework_quality_scanner-0.4.0}/setup.py +7 -2
- robotframework_quality_scanner-0.4.0/src/analyzers/__init__.py +12 -0
- robotframework_quality_scanner-0.4.0/src/models/__init__.py +6 -0
- robotframework_quality_scanner-0.4.0/src/models/issue.py +22 -0
- robotframework_quality_scanner-0.4.0/src/reporters/__init__.py +10 -0
- robotframework_quality_scanner-0.4.0/src/reporters/console_reporter.py +7 -0
- robotframework_quality_scanner-0.4.0/src/reporters/coverage_report.py +313 -0
- robotframework_quality_scanner-0.4.0/src/reporters/executive_report.py +381 -0
- {robotframework-quality-scanner-0.3.0 → robotframework_quality_scanner-0.4.0/src/robotframework_quality_scanner.egg-info}/PKG-INFO +19 -5
- robotframework_quality_scanner-0.4.0/src/robotframework_quality_scanner.egg-info/SOURCES.txt +25 -0
- robotframework_quality_scanner-0.4.0/src/robotframework_quality_scanner.egg-info/entry_points.txt +3 -0
- robotframework_quality_scanner-0.4.0/src/robotframework_quality_scanner.egg-info/top_level.txt +5 -0
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner → robotframework_quality_scanner-0.4.0/src}/utils/__init__.py +7 -0
- robotframework_quality_scanner-0.4.0/src/utils/logger.py +260 -0
- robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner/__init__.py +0 -5
- robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner/analyzers/__init__.py +0 -1
- robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner/scanner.py +0 -173
- robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner.egg-info/SOURCES.txt +0 -19
- robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner.egg-info/top_level.txt +0 -1
- {robotframework-quality-scanner-0.3.0 → robotframework_quality_scanner-0.4.0}/pyproject.toml +0 -0
- {robotframework-quality-scanner-0.3.0 → robotframework_quality_scanner-0.4.0}/setup.cfg +0 -0
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner → robotframework_quality_scanner-0.4.0/src}/analyzers/dependency_analyzer.py +0 -0
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner → robotframework_quality_scanner-0.4.0/src}/analyzers/duplication_analyzer.py +0 -0
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner → robotframework_quality_scanner-0.4.0/src}/analyzers/performance_analyzer.py +0 -0
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner → robotframework_quality_scanner-0.4.0/src}/analyzers/test_data_analyzer.py +0 -0
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner → robotframework_quality_scanner-0.4.0/src}/api/__init__.py +0 -0
- {robotframework-quality-scanner-0.3.0 → robotframework_quality_scanner-0.4.0}/src/robotframework_quality_scanner.egg-info/dependency_links.txt +0 -0
- {robotframework-quality-scanner-0.3.0 → robotframework_quality_scanner-0.4.0}/src/robotframework_quality_scanner.egg-info/requires.txt +0 -0
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner → robotframework_quality_scanner-0.4.0/src}/utils/autofix.py +0 -0
- {robotframework-quality-scanner-0.3.0/src/robotframework_quality_scanner → robotframework_quality_scanner-0.4.0/src}/utils/history.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
|
-
Name:
|
|
3
|
-
Version: 0.
|
|
2
|
+
Name: robotframework_quality_scanner
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Quality scanner for Robot Framework automation - static analysis, performance, duplication detection, and automatic report generation
|
|
5
5
|
Home-page: https://github.com/luisPinheiro536/qa-static-analysis
|
|
6
6
|
Author: Luis
|
|
@@ -13,8 +13,8 @@ Description: # robotframework-quality-scanner
|
|
|
13
13
|
|
|
14
14
|
Uma **Robot Framework Library** para escanear projetos de automação **Web, Mobile e API**, identificar **más práticas**, gerar **logs estruturados**, **relatórios** e **sugestões de correção baseadas em boas práticas oficiais**.
|
|
15
15
|
|
|
16
|
-
**Versão**: 0.
|
|
17
|
-
**Status**: Beta com suporte para caching, histórico, 4 analisadores especializados e
|
|
16
|
+
**Versão**: 0.3.0
|
|
17
|
+
**Status**: Beta com suporte para caching, histórico, 4 analisadores especializados, API REST e logging estruturado com documentação automática.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -27,11 +27,12 @@ Description: # robotframework-quality-scanner
|
|
|
27
27
|
* **Cache 10x mais rápido** para análises repetidas
|
|
28
28
|
* Rastrear **histórico e tendências** de qualidade
|
|
29
29
|
* Gerar **múltiplos relatórios** (JSON, HTML, TXT)
|
|
30
|
+
* **Capturar logs, erros e traces** estruturados em documentação automática
|
|
30
31
|
* Integrar facilmente com **CI/CD e ferramentas externas** via API REST
|
|
31
32
|
|
|
32
33
|
---
|
|
33
34
|
|
|
34
|
-
## ✨ Features v0.
|
|
35
|
+
## ✨ Features v0.3.0
|
|
35
36
|
|
|
36
37
|
### ✅ Implementado
|
|
37
38
|
|
|
@@ -56,7 +57,20 @@ Description: # robotframework-quality-scanner
|
|
|
56
57
|
- Adiciona [Documentation]
|
|
57
58
|
- Capitaliza keywords
|
|
58
59
|
|
|
59
|
-
5. **
|
|
60
|
+
5. **Relatórios Executivos e de Cobertura** (v0.3.0)
|
|
61
|
+
- Score de qualidade (0-100)
|
|
62
|
+
- Distribuição por severidade e categoria
|
|
63
|
+
- Cobertura de testes
|
|
64
|
+
- Formatos: Text, JSON, HTML
|
|
65
|
+
|
|
66
|
+
6. **Logging Estruturado com Documentação** (v0.3.0)
|
|
67
|
+
- Captura automática de erros e traces
|
|
68
|
+
- Relatórios em Markdown com formatação
|
|
69
|
+
- Exportação em JSON para análise programática
|
|
70
|
+
- Pasta `.docs/` para armazenar documentação
|
|
71
|
+
- Histórico de todas as execuções
|
|
72
|
+
|
|
73
|
+
7. **API REST**
|
|
60
74
|
- Endpoints para análise de arquivo/diretório
|
|
61
75
|
- Geração de relatórios (JSON, HTML, TXT)
|
|
62
76
|
- Health check e sumário
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Uma **Robot Framework Library** para escanear projetos de automação **Web, Mobile e API**, identificar **más práticas**, gerar **logs estruturados**, **relatórios** e **sugestões de correção baseadas em boas práticas oficiais**.
|
|
4
4
|
|
|
5
|
-
**Versão**: 0.
|
|
6
|
-
**Status**: Beta com suporte para caching, histórico, 4 analisadores especializados e
|
|
5
|
+
**Versão**: 0.3.0
|
|
6
|
+
**Status**: Beta com suporte para caching, histórico, 4 analisadores especializados, API REST e logging estruturado com documentação automática.
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -16,11 +16,12 @@ Uma **Robot Framework Library** para escanear projetos de automação **Web, Mob
|
|
|
16
16
|
* **Cache 10x mais rápido** para análises repetidas
|
|
17
17
|
* Rastrear **histórico e tendências** de qualidade
|
|
18
18
|
* Gerar **múltiplos relatórios** (JSON, HTML, TXT)
|
|
19
|
+
* **Capturar logs, erros e traces** estruturados em documentação automática
|
|
19
20
|
* Integrar facilmente com **CI/CD e ferramentas externas** via API REST
|
|
20
21
|
|
|
21
22
|
---
|
|
22
23
|
|
|
23
|
-
## ✨ Features v0.
|
|
24
|
+
## ✨ Features v0.3.0
|
|
24
25
|
|
|
25
26
|
### ✅ Implementado
|
|
26
27
|
|
|
@@ -45,7 +46,20 @@ Uma **Robot Framework Library** para escanear projetos de automação **Web, Mob
|
|
|
45
46
|
- Adiciona [Documentation]
|
|
46
47
|
- Capitaliza keywords
|
|
47
48
|
|
|
48
|
-
5. **
|
|
49
|
+
5. **Relatórios Executivos e de Cobertura** (v0.3.0)
|
|
50
|
+
- Score de qualidade (0-100)
|
|
51
|
+
- Distribuição por severidade e categoria
|
|
52
|
+
- Cobertura de testes
|
|
53
|
+
- Formatos: Text, JSON, HTML
|
|
54
|
+
|
|
55
|
+
6. **Logging Estruturado com Documentação** (v0.3.0)
|
|
56
|
+
- Captura automática de erros e traces
|
|
57
|
+
- Relatórios em Markdown com formatação
|
|
58
|
+
- Exportação em JSON para análise programática
|
|
59
|
+
- Pasta `.docs/` para armazenar documentação
|
|
60
|
+
- Histórico de todas as execuções
|
|
61
|
+
|
|
62
|
+
7. **API REST**
|
|
49
63
|
- Endpoints para análise de arquivo/diretório
|
|
50
64
|
- Geração de relatórios (JSON, HTML, TXT)
|
|
51
65
|
- Health check e sumário
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
from setuptools import setup, find_packages
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
|
-
name="
|
|
8
|
-
version="0.
|
|
7
|
+
name="robotframework_quality_scanner",
|
|
8
|
+
version="0.4.0",
|
|
9
9
|
author="Luis",
|
|
10
10
|
author_email="luis@example.com",
|
|
11
11
|
description="Quality scanner for Robot Framework automation - static analysis, performance, duplication detection, and automatic report generation",
|
|
@@ -29,6 +29,11 @@ setup(
|
|
|
29
29
|
"api": ["flask>=2.0"],
|
|
30
30
|
"all": ["pytest>=7.0", "black>=22.0", "flake8>=4.0", "flask>=2.0"],
|
|
31
31
|
},
|
|
32
|
+
entry_points={
|
|
33
|
+
"console_scripts": [
|
|
34
|
+
"qa-scanner=robotframework_quality_scanner.cli:main",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
32
37
|
keywords=["robotframework", "quality", "testing", "static-analysis", "automation"],
|
|
33
38
|
classifiers=[
|
|
34
39
|
"Development Status :: 4 - Beta",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Analisadores especializados
|
|
2
|
+
from .performance_analyzer import PerformanceAnalyzer
|
|
3
|
+
from .duplication_analyzer import DuplicationAnalyzer
|
|
4
|
+
from .dependency_analyzer import DependencyAnalyzer
|
|
5
|
+
from .test_data_analyzer import TestDataAnalyzer
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'PerformanceAnalyzer',
|
|
9
|
+
'DuplicationAnalyzer',
|
|
10
|
+
'DependencyAnalyzer',
|
|
11
|
+
'TestDataAnalyzer',
|
|
12
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class Issue:
|
|
2
|
+
def __init__(self, rule_id, category, severity, description, file, line, recommendation, reference=None):
|
|
3
|
+
self.rule_id = rule_id
|
|
4
|
+
self.category = category
|
|
5
|
+
self.severity = severity
|
|
6
|
+
self.description = description
|
|
7
|
+
self.file = file
|
|
8
|
+
self.line = line
|
|
9
|
+
self.recommendation = recommendation
|
|
10
|
+
self.reference = reference
|
|
11
|
+
|
|
12
|
+
def to_dict(self):
|
|
13
|
+
return {
|
|
14
|
+
"rule_id": self.rule_id,
|
|
15
|
+
"category": self.category,
|
|
16
|
+
"severity": self.severity,
|
|
17
|
+
"description": self.description,
|
|
18
|
+
"file": self.file,
|
|
19
|
+
"line": self.line,
|
|
20
|
+
"recommendation": self.recommendation,
|
|
21
|
+
"reference": self.reference,
|
|
22
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""Relatório de cobertura de testes."""
|
|
2
|
+
import re
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CoverageReport:
|
|
7
|
+
"""Analisa cobertura de testes em arquivos Robot Framework."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, files_analyzed):
|
|
10
|
+
"""
|
|
11
|
+
Args:
|
|
12
|
+
files_analyzed: Lista de tuplas (filepath, content)
|
|
13
|
+
"""
|
|
14
|
+
self.files_analyzed = files_analyzed
|
|
15
|
+
self.coverage_data = self._analyze_coverage()
|
|
16
|
+
|
|
17
|
+
def _analyze_coverage(self):
|
|
18
|
+
"""Analisa cobertura em cada arquivo."""
|
|
19
|
+
coverage = defaultdict(lambda: {
|
|
20
|
+
"total_keywords": 0,
|
|
21
|
+
"documented_keywords": 0,
|
|
22
|
+
"used_keywords": 0,
|
|
23
|
+
"unused_keywords": [],
|
|
24
|
+
"test_count": 0,
|
|
25
|
+
"keyword_count": 0,
|
|
26
|
+
"documentation_coverage": 0,
|
|
27
|
+
"keyword_usage_coverage": 0,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
for filepath, content in self.files_analyzed:
|
|
31
|
+
file_key = filepath if isinstance(filepath, str) else filepath.name
|
|
32
|
+
|
|
33
|
+
# Parse sections
|
|
34
|
+
in_keywords = False
|
|
35
|
+
in_tests = False
|
|
36
|
+
keywords_defined = []
|
|
37
|
+
keywords_used = set()
|
|
38
|
+
documented_kw = 0
|
|
39
|
+
test_count = 0
|
|
40
|
+
|
|
41
|
+
lines = content.split('\n')
|
|
42
|
+
for i, line in enumerate(lines):
|
|
43
|
+
# Detectar seções
|
|
44
|
+
if '*** Keywords ***' in line:
|
|
45
|
+
in_keywords = True
|
|
46
|
+
in_tests = False
|
|
47
|
+
continue
|
|
48
|
+
elif '*** Test Cases ***' in line:
|
|
49
|
+
in_tests = True
|
|
50
|
+
in_keywords = False
|
|
51
|
+
continue
|
|
52
|
+
elif '*** Settings ***' in line or '*** Variables ***' in line:
|
|
53
|
+
in_keywords = False
|
|
54
|
+
in_tests = False
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
# Contar keywords definidas
|
|
58
|
+
if in_keywords and line.strip() and not line.startswith(' '):
|
|
59
|
+
keywords_defined.append(line.strip())
|
|
60
|
+
# Verificar se tem documentation
|
|
61
|
+
if i + 1 < len(lines) and '[Documentation]' in lines[i + 1]:
|
|
62
|
+
documented_kw += 1
|
|
63
|
+
|
|
64
|
+
# Contar testes
|
|
65
|
+
if in_tests and line.strip() and not line.startswith(' '):
|
|
66
|
+
test_count += 1
|
|
67
|
+
|
|
68
|
+
# Coletar keywords usadas (heurística)
|
|
69
|
+
for kw in keywords_defined:
|
|
70
|
+
if kw in line and not in_keywords:
|
|
71
|
+
keywords_used.add(kw)
|
|
72
|
+
|
|
73
|
+
# Calcular métricas
|
|
74
|
+
total_kw = len(keywords_defined)
|
|
75
|
+
used_kw = len(keywords_used)
|
|
76
|
+
unused_kw = [kw for kw in keywords_defined if kw not in keywords_used]
|
|
77
|
+
|
|
78
|
+
coverage[file_key] = {
|
|
79
|
+
"total_keywords": total_kw,
|
|
80
|
+
"documented_keywords": documented_kw,
|
|
81
|
+
"used_keywords": used_kw,
|
|
82
|
+
"unused_keywords": unused_kw,
|
|
83
|
+
"test_count": test_count,
|
|
84
|
+
"documentation_coverage": (documented_kw / total_kw * 100) if total_kw > 0 else 0,
|
|
85
|
+
"keyword_usage_coverage": (used_kw / total_kw * 100) if total_kw > 0 else 0,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return dict(coverage)
|
|
89
|
+
|
|
90
|
+
def get_overall_stats(self):
|
|
91
|
+
"""Estatísticas gerais de cobertura."""
|
|
92
|
+
total_files = len(self.coverage_data)
|
|
93
|
+
total_keywords = sum(d["total_keywords"] for d in self.coverage_data.values())
|
|
94
|
+
total_documented = sum(d["documented_keywords"] for d in self.coverage_data.values())
|
|
95
|
+
total_used = sum(d["used_keywords"] for d in self.coverage_data.values())
|
|
96
|
+
total_tests = sum(d["test_count"] for d in self.coverage_data.values())
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
"files": total_files,
|
|
100
|
+
"total_keywords": total_keywords,
|
|
101
|
+
"documented_keywords": total_documented,
|
|
102
|
+
"used_keywords": total_used,
|
|
103
|
+
"total_tests": total_tests,
|
|
104
|
+
"documentation_coverage_percent": (total_documented / total_keywords * 100) if total_keywords > 0 else 0,
|
|
105
|
+
"keyword_usage_coverage_percent": (total_used / total_keywords * 100) if total_keywords > 0 else 0,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
def to_dict(self):
|
|
109
|
+
"""Exporta como dicionário."""
|
|
110
|
+
return {
|
|
111
|
+
"overall": self.get_overall_stats(),
|
|
112
|
+
"by_file": self.coverage_data,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
def to_text(self):
|
|
116
|
+
"""Exporta como texto."""
|
|
117
|
+
stats = self.get_overall_stats()
|
|
118
|
+
|
|
119
|
+
text = "╔" + "=" * 78 + "╗\n"
|
|
120
|
+
text += "║" + " RELATÓRIO DE COBERTURA DE TESTES ".center(78) + "║\n"
|
|
121
|
+
text += "╚" + "=" * 78 + "╝\n\n"
|
|
122
|
+
|
|
123
|
+
# Resumo geral
|
|
124
|
+
text += "📊 RESUMO GERAL\n"
|
|
125
|
+
text += "-" * 80 + "\n"
|
|
126
|
+
text += f"Arquivos Analisados: {stats['files']}\n"
|
|
127
|
+
text += f"Total de Keywords: {stats['total_keywords']}\n"
|
|
128
|
+
text += f"Keywords Documentadas: {stats['documented_keywords']} ({stats['documentation_coverage_percent']:.1f}%)\n"
|
|
129
|
+
text += f"Keywords Utilizadas: {stats['used_keywords']} ({stats['keyword_usage_coverage_percent']:.1f}%)\n"
|
|
130
|
+
text += f"Total de Testes: {stats['total_tests']}\n\n"
|
|
131
|
+
|
|
132
|
+
# Por arquivo
|
|
133
|
+
text += "📄 POR ARQUIVO\n"
|
|
134
|
+
text += "-" * 80 + "\n"
|
|
135
|
+
for file, data in sorted(self.coverage_data.items()):
|
|
136
|
+
text += f"\n{file}\n"
|
|
137
|
+
text += f" Keywords: {data['total_keywords']} (Docs: {data['documentation_coverage']:.0f}%, Uso: {data['keyword_usage_coverage']:.0f}%)\n"
|
|
138
|
+
text += f" Testes: {data['test_count']}\n"
|
|
139
|
+
|
|
140
|
+
if data['unused_keywords']:
|
|
141
|
+
text += f" ⚠️ Keywords não utilizadas: {', '.join(data['unused_keywords'][:5])}\n"
|
|
142
|
+
|
|
143
|
+
text += "\n" + "=" * 80 + "\n"
|
|
144
|
+
|
|
145
|
+
return text
|
|
146
|
+
|
|
147
|
+
def to_html(self):
|
|
148
|
+
"""Exporta como HTML."""
|
|
149
|
+
stats = self.get_overall_stats()
|
|
150
|
+
|
|
151
|
+
doc_color = "#27ae60" if stats["documentation_coverage_percent"] >= 80 else "#f39c12" if stats["documentation_coverage_percent"] >= 60 else "#e74c3c"
|
|
152
|
+
usage_color = "#27ae60" if stats["keyword_usage_coverage_percent"] >= 80 else "#f39c12" if stats["keyword_usage_coverage_percent"] >= 60 else "#e74c3c"
|
|
153
|
+
|
|
154
|
+
html = f"""
|
|
155
|
+
<html>
|
|
156
|
+
<head>
|
|
157
|
+
<meta charset="UTF-8">
|
|
158
|
+
<title>Relatório de Cobertura</title>
|
|
159
|
+
<style>
|
|
160
|
+
body {{
|
|
161
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
162
|
+
margin: 0;
|
|
163
|
+
padding: 20px;
|
|
164
|
+
background: #f5f5f5;
|
|
165
|
+
}}
|
|
166
|
+
.container {{
|
|
167
|
+
max-width: 1200px;
|
|
168
|
+
margin: 0 auto;
|
|
169
|
+
background: white;
|
|
170
|
+
border-radius: 8px;
|
|
171
|
+
padding: 30px;
|
|
172
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
173
|
+
}}
|
|
174
|
+
h1 {{
|
|
175
|
+
color: #2c3e50;
|
|
176
|
+
border-bottom: 3px solid #9b59b6;
|
|
177
|
+
padding-bottom: 10px;
|
|
178
|
+
}}
|
|
179
|
+
.metrics {{
|
|
180
|
+
display: grid;
|
|
181
|
+
grid-template-columns: repeat(5, 1fr);
|
|
182
|
+
gap: 15px;
|
|
183
|
+
margin: 20px 0;
|
|
184
|
+
}}
|
|
185
|
+
.metric-card {{
|
|
186
|
+
background: #ecf0f1;
|
|
187
|
+
padding: 20px;
|
|
188
|
+
border-radius: 5px;
|
|
189
|
+
text-align: center;
|
|
190
|
+
}}
|
|
191
|
+
.metric-card h3 {{
|
|
192
|
+
margin: 0;
|
|
193
|
+
color: #7f8c8d;
|
|
194
|
+
font-size: 12px;
|
|
195
|
+
text-transform: uppercase;
|
|
196
|
+
}}
|
|
197
|
+
.metric-card .value {{
|
|
198
|
+
font-size: 28px;
|
|
199
|
+
font-weight: bold;
|
|
200
|
+
color: #2c3e50;
|
|
201
|
+
}}
|
|
202
|
+
.coverage-bar {{
|
|
203
|
+
width: 100%;
|
|
204
|
+
height: 30px;
|
|
205
|
+
background: #ecf0f1;
|
|
206
|
+
border-radius: 4px;
|
|
207
|
+
overflow: hidden;
|
|
208
|
+
margin: 10px 0;
|
|
209
|
+
}}
|
|
210
|
+
.coverage-fill {{
|
|
211
|
+
height: 100%;
|
|
212
|
+
display: flex;
|
|
213
|
+
align-items: center;
|
|
214
|
+
justify-content: center;
|
|
215
|
+
color: white;
|
|
216
|
+
font-weight: bold;
|
|
217
|
+
font-size: 12px;
|
|
218
|
+
}}
|
|
219
|
+
table {{
|
|
220
|
+
width: 100%;
|
|
221
|
+
border-collapse: collapse;
|
|
222
|
+
margin: 20px 0;
|
|
223
|
+
}}
|
|
224
|
+
th, td {{
|
|
225
|
+
padding: 12px;
|
|
226
|
+
text-align: left;
|
|
227
|
+
border-bottom: 1px solid #ecf0f1;
|
|
228
|
+
}}
|
|
229
|
+
th {{
|
|
230
|
+
background: #34495e;
|
|
231
|
+
color: white;
|
|
232
|
+
}}
|
|
233
|
+
tr:hover {{
|
|
234
|
+
background: #f8f9fa;
|
|
235
|
+
}}
|
|
236
|
+
</style>
|
|
237
|
+
</head>
|
|
238
|
+
<body>
|
|
239
|
+
<div class="container">
|
|
240
|
+
<h1>📊 Relatório de Cobertura de Testes</h1>
|
|
241
|
+
|
|
242
|
+
<div class="metrics">
|
|
243
|
+
<div class="metric-card">
|
|
244
|
+
<h3>Arquivos</h3>
|
|
245
|
+
<div class="value">{stats['files']}</div>
|
|
246
|
+
</div>
|
|
247
|
+
<div class="metric-card">
|
|
248
|
+
<h3>Keywords</h3>
|
|
249
|
+
<div class="value">{stats['total_keywords']}</div>
|
|
250
|
+
</div>
|
|
251
|
+
<div class="metric-card">
|
|
252
|
+
<h3>Documentação</h3>
|
|
253
|
+
<div class="value">{stats['documentation_coverage_percent']:.0f}%</div>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="metric-card">
|
|
256
|
+
<h3>Utilização</h3>
|
|
257
|
+
<div class="value">{stats['keyword_usage_coverage_percent']:.0f}%</div>
|
|
258
|
+
</div>
|
|
259
|
+
<div class="metric-card">
|
|
260
|
+
<h3>Testes</h3>
|
|
261
|
+
<div class="value">{stats['total_tests']}</div>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<h2>Cobertura Geral</h2>
|
|
266
|
+
<div>
|
|
267
|
+
<strong>Documentação</strong>
|
|
268
|
+
<div class="coverage-bar">
|
|
269
|
+
<div class="coverage-fill" style="width: {stats['documentation_coverage_percent']}%; background: {doc_color};">
|
|
270
|
+
{stats['documentation_coverage_percent']:.1f}%
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
<div>
|
|
276
|
+
<strong>Utilização de Keywords</strong>
|
|
277
|
+
<div class="coverage-bar">
|
|
278
|
+
<div class="coverage-fill" style="width: {stats['keyword_usage_coverage_percent']}%; background: {usage_color};">
|
|
279
|
+
{stats['keyword_usage_coverage_percent']:.1f}%
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
<h2>Cobertura por Arquivo</h2>
|
|
285
|
+
<table>
|
|
286
|
+
<tr>
|
|
287
|
+
<th>Arquivo</th>
|
|
288
|
+
<th>Keywords</th>
|
|
289
|
+
<th>Documentação</th>
|
|
290
|
+
<th>Utilização</th>
|
|
291
|
+
<th>Testes</th>
|
|
292
|
+
</tr>
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
for file, data in sorted(self.coverage_data.items()):
|
|
296
|
+
html += f"""
|
|
297
|
+
<tr>
|
|
298
|
+
<td>{file}</td>
|
|
299
|
+
<td>{data['total_keywords']}</td>
|
|
300
|
+
<td>{data['documentation_coverage']:.0f}%</td>
|
|
301
|
+
<td>{data['keyword_usage_coverage']:.0f}%</td>
|
|
302
|
+
<td>{data['test_count']}</td>
|
|
303
|
+
</tr>
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
html += """
|
|
307
|
+
</table>
|
|
308
|
+
</div>
|
|
309
|
+
</body>
|
|
310
|
+
</html>
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
return html
|