#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Standalone property-based test for preview generation. This test verifies that preview generation works correctly by testing the certificate generation logic with sample data. Feature: survey-custom-certificate-template, Property 9: Preview generation Validates: Requirements 4.2 """ import sys import os import io # Add parent directory to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) try: from hypothesis import given, settings, HealthCheck HYPOTHESIS_AVAILABLE = True except ImportError: print("ERROR: Hypothesis is not installed. Install with: pip install hypothesis") sys.exit(1) try: from docx import Document DOCX_AVAILABLE = True except ImportError: print("ERROR: python-docx is not installed. Install with: pip install python-docx") sys.exit(1) # Import our custom strategies from hypothesis_strategies import docx_with_mappings_and_data def generate_sample_data(): """ Generate sample data for preview (mimics wizard's _generate_sample_data). """ from datetime import datetime return { 'survey_title': 'Sample Course Title', 'survey_description': 'Sample course description', 'partner_name': 'John Doe', 'partner_email': 'john.doe@example.com', 'email': 'john.doe@example.com', 'create_date': datetime.now().strftime('%Y-%m-%d'), 'completion_date': datetime.now().strftime('%Y-%m-%d'), 'scoring_percentage': '95.5', 'scoring_total': '100', } def replace_placeholders_in_docx(docx_binary, mappings, data): """ Replace placeholders in a DOCX document with data. This simulates the certificate generator's placeholder replacement logic. """ # Load the document doc_stream = io.BytesIO(docx_binary) doc = Document(doc_stream) # Build a replacement dictionary replacements = {} for mapping in mappings['placeholders']: key = mapping['key'] value_type = mapping['value_type'] if value_type == 'custom_text': # Use custom text replacements[key] = mapping.get('custom_text', '') else: # Use data from the data dictionary value_field = mapping.get('value_field', '') if value_field and value_field in data: replacements[key] = str(data[value_field]) else: # Use empty string for unmapped fields replacements[key] = '' # Replace placeholders in paragraphs for paragraph in doc.paragraphs: for key, value in replacements.items(): if key in paragraph.text: # Replace in the paragraph text for run in paragraph.runs: if key in run.text: run.text = run.text.replace(key, value) # Replace placeholders in tables for table in doc.tables: for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: for key, value in replacements.items(): if key in paragraph.text: for run in paragraph.runs: if key in run.text: run.text = run.text.replace(key, value) # Replace placeholders in headers/footers for section in doc.sections: # Header for paragraph in section.header.paragraphs: for key, value in replacements.items(): if key in paragraph.text: for run in paragraph.runs: if key in run.text: run.text = run.text.replace(key, value) # Footer for paragraph in section.footer.paragraphs: for key, value in replacements.items(): if key in paragraph.text: for run in paragraph.runs: if key in run.text: run.text = run.text.replace(key, value) # Save to bytes output_stream = io.BytesIO() doc.save(output_stream) output_stream.seek(0) return output_stream.read() def extract_all_text_from_docx(docx_binary): """Extract all text content from a DOCX document.""" doc_stream = io.BytesIO(docx_binary) doc = Document(doc_stream) text_parts = [] # Extract from paragraphs for paragraph in doc.paragraphs: text_parts.append(paragraph.text) # Extract from tables for table in doc.tables: for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: text_parts.append(paragraph.text) # Extract from headers/footers for section in doc.sections: for paragraph in section.header.paragraphs: text_parts.append(paragraph.text) for paragraph in section.footer.paragraphs: text_parts.append(paragraph.text) return ' '.join(text_parts) def test_property_9_preview_generation(): """ Feature: survey-custom-certificate-template, Property 9: Preview generation For any configured template, clicking the preview button should generate a sample certificate with all placeholders replaced. Validates: Requirements 4.2 This property test verifies that: 1. Preview generation succeeds for any valid template with mappings 2. The generated preview is a valid DOCX document 3. All placeholders in the template are replaced (no placeholders remain) 4. The preview contains either sample data or custom text for each placeholder """ print("\nTesting Property 9: Preview generation") print("=" * 60) test_count = 0 failures = [] @given(test_case=docx_with_mappings_and_data()) @settings( max_examples=100, deadline=None, suppress_health_check=[HealthCheck.function_scoped_fixture] ) def check_preview_generation(test_case): nonlocal test_count, failures test_count += 1 docx_binary, mappings, data = test_case # Generate sample data (as the wizard would do) sample_data = generate_sample_data() # Merge with test data to ensure we have all fields for key, value in data.items(): if key not in sample_data: sample_data[key] = value # Property 1: Preview generation should succeed try: preview_docx = replace_placeholders_in_docx(docx_binary, mappings, sample_data) except Exception as e: failures.append(f"Preview generation failed: {e}") raise AssertionError(f"Preview generation should succeed for any valid template: {e}") # Property 2: The generated preview should be a valid DOCX try: preview_stream = io.BytesIO(preview_docx) preview_doc = Document(preview_stream) # If we can load it, it's valid except Exception as e: failures.append(f"Generated preview is not a valid DOCX: {e}") raise AssertionError(f"Generated preview should be a valid DOCX document: {e}") # Property 3: All placeholders should be replaced (no placeholders remain) preview_text = extract_all_text_from_docx(preview_docx) # Check for any remaining placeholders import re remaining_placeholders = re.findall(r'\{key\.[a-zA-Z0-9_]+\}', preview_text) if remaining_placeholders: failures.append( f"Preview contains unreplaced placeholders: {remaining_placeholders}" ) raise AssertionError( f"All placeholders should be replaced in preview. " f"Found unreplaced: {remaining_placeholders}" ) # Property 4: The preview should contain expected data # For each mapping, verify that the replacement value appears in the preview for mapping in mappings['placeholders']: value_type = mapping['value_type'] if value_type == 'custom_text': # Should contain the custom text expected_value = mapping.get('custom_text', '') if expected_value and expected_value not in preview_text: # Only fail if the custom text is non-empty failures.append( f"Preview missing custom text '{expected_value}' for {mapping['key']}" ) raise AssertionError( f"Preview should contain custom text '{expected_value}' " f"for placeholder {mapping['key']}" ) else: # Should contain data from sample_data value_field = mapping.get('value_field', '') if value_field and value_field in sample_data: expected_value = str(sample_data[value_field]) if expected_value and expected_value not in preview_text: # Only fail if the expected value is non-empty failures.append( f"Preview missing data '{expected_value}' for {mapping['key']}" ) raise AssertionError( f"Preview should contain data '{expected_value}' " f"for placeholder {mapping['key']}" ) try: check_preview_generation() print(f"✓ Property 9 verified across {test_count} test cases") print(" All preview generations succeeded with placeholders replaced") print(f" Tested with various template structures and mapping configurations") return True except Exception as e: print(f"✗ Property 9 FAILED after {test_count} test cases") print(f" Error: {e}") if failures: print(f"\n Failure details:") for failure in failures[:5]: # Show first 5 failures print(f" - {failure}") import traceback traceback.print_exc() return False def main(): """Run the property test.""" print("=" * 60) print("Property-Based Test: Preview Generation") print("=" * 60) print("\nThis test verifies that preview generation works correctly") print("for any configured template with placeholder mappings.") print("\nTesting scenarios:") print(" - Various template structures (paragraphs, tables, headers/footers)") print(" - Different numbers of placeholders (1-8)") print(" - Different value types (survey_field, user_field, custom_text)") print(" - Placeholder replacement completeness") print(" - Valid DOCX output") success = test_property_9_preview_generation() print("\n" + "=" * 60) if success: print("✓ Property test PASSED") print("=" * 60) print("\nConclusion:") print(" The preview generation mechanism correctly creates sample") print(" certificates with all placeholders replaced for any valid") print(" template configuration.") return 0 else: print("✗ Property test FAILED") print("=" * 60) print("\nThe preview generation mechanism has issues that need to be addressed.") return 1 if __name__ == '__main__': sys.exit(main())