diff --git a/src/xmlvalidator/XmlValidator.py b/src/xmlvalidator/XmlValidator.py index 6fc261d..1ed1a06 100644 --- a/src/xmlvalidator/XmlValidator.py +++ b/src/xmlvalidator/XmlValidator.py @@ -402,7 +402,7 @@ def __init__( xsd_path: str | Path | None = None, base_url: str | None = None, error_facets: List[str] | None = None, - fail_on_errors: bool = True + fail_on_errors: bool = True, ) -> None: """ **Library Scope** @@ -1460,7 +1460,8 @@ def validate_xml_files( # pylint: disable=R0913:too-many-arguments disable=R0914 write_to_csv: Optional[bool] = True, timestamped: Optional[bool] = True, reset_errors: bool = True, - fail_on_errors: Optional[bool] = None + fail_on_errors: Optional[bool] = None, + error_table: Optional[bool] = True ) -> Tuple[ List[ Dict[str, Any] ], str | None @@ -1591,6 +1592,11 @@ def validate_xml_files( # pylint: disable=R0913:too-many-arguments disable=R0914 XML files, one or more errors have been reported. Error reporting and exporting will not change. + ``error_table`` + + If True, writes all collected errors to a filterable table in + the log file. Defaults to True. + **Returns** A tuple, holding: @@ -1670,6 +1676,11 @@ def validate_xml_files( # pylint: disable=R0913:too-many-arguments disable=R0914 ) else: csv_path = None + # Write errors to the log file as a table if requested. + if error_table and self.validator_results.errors_by_file: + self.validator_results.write_error_table_to_log( + self.validator_results.errors_by_file, + ) # Log a summary of the test run. self.validator_results.log_summary() if fail_on_errors and self.validator_results.errors_by_file: diff --git a/src/xmlvalidator/xml_validator_results.py b/src/xmlvalidator/xml_validator_results.py index df3182b..1c0ac21 100644 --- a/src/xmlvalidator/xml_validator_results.py +++ b/src/xmlvalidator/xml_validator_results.py @@ -81,6 +81,60 @@ class ValidatorResultRecorder: validation_summary: Dict[str, List[str]] = field( default_factory=lambda: {"valid": [], "invalid": []} ) + # Used to create unique error_tables in the log file if multiple tables are present. + error_table_id = 0 + # Used styling to use the same theme as Robot Framework uses. + style_and_filter_script = """ + + + """ def _get_summary(self) -> Dict[str, int]: """ @@ -340,6 +394,52 @@ def write_errors_to_csv(self, ) from e return str( output_csv_path.resolve() ) + def write_error_table_to_log(self, errors: List[ Dict[str, Any] ]): + """ + Writes a table of validation errors to the log file. + + This method takes a list of error dictionaries and writes them + to the log file in a table format. It also adds an input that + can be used to filter through the errors and updates in real + time. + + Args: + + - errors (List[Dict[str, Any]]): + A list of dictionaries, where each dictionary contains details + of a validation error. Each key in the dictionaries + corresponds to a column in the output CSV. + + Notes: + + - If `errors` is an empty list, the method exits early and logs + an informational message without creating a file. + the error dictionaries. + - The method uses `pandas` for CSV generation. + """ + # Return if no errors were passed. + if not errors: + logger.info("No errors to write to log file.") + return + # Convert the errors list to a DataFrame. + df = pd.DataFrame(errors) + # Convert the dataframe to HTML. + df_table = df.to_html(index=False, border=0) + # Get the table id and increment for the next one. + error_table_id = self.error_table_id + self.error_table_id += 1 + # Add the filter input to the df_table (includes the function call) + full_html = f"""
+ + {df_table} +
""" + # Add the style and filter script if it is the first table + if error_table_id == 0: + full_html = f"{full_html}{self.style_and_filter_script}" + # Actually print the table to the log file + logger.info(full_html, html=True) + + class ValidatorResult: # pylint: disable=R0903:too-few-public-methods """ Encapsulates the result of an operation in a success-or-failure format. diff --git a/test/_data/integration/TC_15/empty_folder/.gitkeep b/test/_data/integration/TC_15/empty_folder/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/_data/integration/TC_18/empty_folder/.gitkeep b/test/_data/integration/TC_18/empty_folder/.gitkeep new file mode 100644 index 0000000..e69de29