Some checks failed
Build & Push Docker / build (push) Has been cancelled
- Nový modul scenar/core.py (491 řádků čisté logiky)
- Refactored cgi-bin/scenar.py (450 řádků CGI wrapper)
- Inline editor s JavaScript row managementem
- Custom exceptions (ScenarsError, ValidationError, TemplateError)
- Kompletní test coverage (10 testů, všechny ✅)
- Fixed Dockerfile (COPY scenar/, requirements.txt)
- Fixed requirements.txt (openpyxl==3.1.5)
- Fixed pytest.ini (pythonpath = .)
- Nové testy: test_http_inline.py, test_inline_builder.py
- HTTP testy označeny jako @pytest.mark.integration
- Build script: scripts/build_image.sh
- Dokumentace: COMPLETION.md
262 lines
7.5 KiB
Python
262 lines
7.5 KiB
Python
"""
|
|
End-to-end tests for inline builder (without Excel upload).
|
|
Tests the full form submission flow from HTML form to timetable generation.
|
|
"""
|
|
|
|
import pytest
|
|
from datetime import date, time
|
|
import pandas as pd
|
|
from scenar.core import parse_inline_schedule, parse_inline_types, create_timetable, ValidationError
|
|
|
|
|
|
def test_inline_builder_valid_form():
|
|
"""Test inline builder with valid schedule and types."""
|
|
# Simulate form data from HTML
|
|
form_data = {
|
|
# Metadata
|
|
'title': 'Test Conference',
|
|
'detail': 'Testing inline builder',
|
|
'step': 'builder',
|
|
|
|
# Schedule rows (2 rows)
|
|
'datum_0': '2025-11-13',
|
|
'zacatek_0': '09:00',
|
|
'konec_0': '10:00',
|
|
'program_0': 'Opening Keynote',
|
|
'typ_0': 'KEYNOTE',
|
|
'garant_0': 'Dr. Smith',
|
|
'poznamka_0': 'Welcome speech',
|
|
|
|
'datum_1': '2025-11-13',
|
|
'zacatek_1': '10:30',
|
|
'konec_1': '11:30',
|
|
'program_1': 'Workshop: Python',
|
|
'typ_1': 'WORKSHOP',
|
|
'garant_1': 'Jane Doe',
|
|
'poznamka_1': 'Hands-on coding',
|
|
|
|
# Type definitions
|
|
'type_name_0': 'KEYNOTE',
|
|
'type_desc_0': 'Main Address',
|
|
'type_color_0': '#FF0000',
|
|
|
|
'type_name_1': 'WORKSHOP',
|
|
'type_desc_1': 'Interactive Session',
|
|
'type_color_1': '#0070C0',
|
|
}
|
|
|
|
# Parse schedule
|
|
schedule = parse_inline_schedule(form_data)
|
|
assert len(schedule) == 2
|
|
assert schedule.iloc[0]['Program'] == 'Opening Keynote'
|
|
assert schedule.iloc[0]['Typ'] == 'KEYNOTE'
|
|
|
|
# Parse types
|
|
descriptions, colors = parse_inline_types(form_data)
|
|
assert len(descriptions) == 2
|
|
assert descriptions['KEYNOTE'] == 'Main Address'
|
|
assert colors['KEYNOTE'] == 'FFFF0000'
|
|
|
|
# Generate timetable
|
|
wb = create_timetable(
|
|
schedule,
|
|
title=form_data['title'],
|
|
detail=form_data['detail'],
|
|
program_descriptions=descriptions,
|
|
program_colors=colors
|
|
)
|
|
|
|
assert wb is not None
|
|
ws = wb.active
|
|
assert ws['A1'].value == 'Test Conference'
|
|
assert ws['A2'].value == 'Testing inline builder'
|
|
|
|
|
|
def test_inline_builder_missing_type_definition():
|
|
"""Test that undefined type in schedule raises error."""
|
|
form_data = {
|
|
'title': 'Bad Conference',
|
|
'detail': 'Missing type definition',
|
|
|
|
# Schedule with UNKNOWN type
|
|
'datum_0': '2025-11-13',
|
|
'zacatek_0': '09:00',
|
|
'konec_0': '10:00',
|
|
'program_0': 'Unknown Program',
|
|
'typ_0': 'UNKNOWN_TYPE', # This type is not defined below!
|
|
'garant_0': 'Someone',
|
|
'poznamka_0': '',
|
|
|
|
# Only define LECTURE, not UNKNOWN_TYPE
|
|
'type_name_0': 'LECTURE',
|
|
'type_desc_0': 'Standard Lecture',
|
|
'type_color_0': '#3498db',
|
|
}
|
|
|
|
schedule = parse_inline_schedule(form_data)
|
|
descriptions, colors = parse_inline_types(form_data)
|
|
|
|
# Should fail because UNKNOWN_TYPE is not in colors dict
|
|
with pytest.raises(Exception): # ScenarsError
|
|
create_timetable(schedule, 'Bad', 'Bad', descriptions, colors)
|
|
|
|
|
|
def test_inline_builder_empty_type_definition():
|
|
"""Test that empty type definitions are skipped."""
|
|
form_data = {
|
|
'title': 'Conference',
|
|
'detail': 'With empty types',
|
|
|
|
'datum_0': '2025-11-13',
|
|
'zacatek_0': '09:00',
|
|
'konec_0': '10:00',
|
|
'program_0': 'Program 1',
|
|
'typ_0': 'WORKSHOP',
|
|
'garant_0': 'John',
|
|
'poznamka_0': '',
|
|
|
|
# One empty type definition (should be skipped)
|
|
'type_name_0': '',
|
|
'type_desc_0': 'Empty type',
|
|
'type_color_0': '#FF0000',
|
|
|
|
# One valid type
|
|
'type_name_1': 'WORKSHOP',
|
|
'type_desc_1': 'Workshop Type',
|
|
'type_color_1': '#0070C0',
|
|
}
|
|
|
|
descriptions, colors = parse_inline_types(form_data)
|
|
|
|
# Empty type should be skipped
|
|
assert 'WORKSHOP' in descriptions
|
|
assert '' not in descriptions
|
|
|
|
|
|
def test_inline_builder_overlapping_times():
|
|
"""Test schedule with overlapping time slots."""
|
|
form_data = {
|
|
'title': 'Conference',
|
|
'detail': 'Overlapping schedule',
|
|
|
|
# Two programs at same time
|
|
'datum_0': '2025-11-13',
|
|
'zacatek_0': '09:00',
|
|
'konec_0': '10:00',
|
|
'program_0': 'Program A',
|
|
'typ_0': 'WORKSHOP',
|
|
'garant_0': 'John',
|
|
'poznamka_0': '',
|
|
|
|
'datum_1': '2025-11-13',
|
|
'zacatek_1': '09:30', # Overlaps with Program A
|
|
'konec_1': '10:30',
|
|
'program_1': 'Program B',
|
|
'typ_1': 'WORKSHOP',
|
|
'garant_1': 'Jane',
|
|
'poznamka_1': '',
|
|
|
|
'type_name_0': 'WORKSHOP',
|
|
'type_desc_0': 'Workshop',
|
|
'type_color_0': '#0070C0',
|
|
}
|
|
|
|
schedule = parse_inline_schedule(form_data)
|
|
descriptions, colors = parse_inline_types(form_data)
|
|
|
|
# Should allow overlapping times — timetable will show them
|
|
assert len(schedule) == 2
|
|
|
|
# Generate timetable (may have rendering issues but should complete)
|
|
wb = create_timetable(schedule, 'Conf', 'Test', descriptions, colors)
|
|
assert wb is not None
|
|
|
|
|
|
def test_inline_builder_multiday():
|
|
"""Test schedule spanning multiple days."""
|
|
form_data = {
|
|
'title': 'Multi-day Conference',
|
|
'detail': 'Two-day event',
|
|
|
|
# Day 1
|
|
'datum_0': '2025-11-13',
|
|
'zacatek_0': '09:00',
|
|
'konec_0': '10:00',
|
|
'program_0': 'Day 1 Opening',
|
|
'typ_0': 'KEYNOTE',
|
|
'garant_0': 'Dr. A',
|
|
'poznamka_0': '',
|
|
|
|
# Day 2
|
|
'datum_1': '2025-11-14',
|
|
'zacatek_1': '09:00',
|
|
'konec_1': '10:00',
|
|
'program_1': 'Day 2 Opening',
|
|
'typ_1': 'KEYNOTE',
|
|
'garant_1': 'Dr. B',
|
|
'poznamka_1': '',
|
|
|
|
'type_name_0': 'KEYNOTE',
|
|
'type_desc_0': 'Keynote Speech',
|
|
'type_color_0': '#FF6600',
|
|
}
|
|
|
|
schedule = parse_inline_schedule(form_data)
|
|
descriptions, colors = parse_inline_types(form_data)
|
|
|
|
wb = create_timetable(schedule, 'Multi-day', 'Test', descriptions, colors)
|
|
|
|
assert wb is not None
|
|
ws = wb.active
|
|
# Should have multiple date rows
|
|
assert ws['A1'].value == 'Multi-day'
|
|
|
|
|
|
def test_inline_builder_validation_errors():
|
|
"""Test validation of inline form data."""
|
|
# Missing required field
|
|
form_data_missing = {
|
|
'datum_0': '2025-11-13',
|
|
# Missing zacatek_0, konec_0, program_0, typ_0
|
|
'garant_0': 'John',
|
|
}
|
|
|
|
with pytest.raises(ValidationError):
|
|
parse_inline_schedule(form_data_missing)
|
|
|
|
|
|
def test_inline_builder_with_empty_rows():
|
|
"""Test that empty schedule rows are skipped."""
|
|
form_data = {
|
|
'title': 'Test',
|
|
'detail': 'With empty rows',
|
|
|
|
# Valid row
|
|
'datum_0': '2025-11-13',
|
|
'zacatek_0': '09:00',
|
|
'konec_0': '10:00',
|
|
'program_0': 'Program 1',
|
|
'typ_0': 'WORKSHOP',
|
|
'garant_0': 'John',
|
|
'poznamka_0': '',
|
|
|
|
# Empty row (all fields missing)
|
|
'datum_1': '',
|
|
'zacatek_1': '',
|
|
'konec_1': '',
|
|
'program_1': '',
|
|
'typ_1': '',
|
|
'garant_1': '',
|
|
'poznamka_1': '',
|
|
|
|
'type_name_0': 'WORKSHOP',
|
|
'type_desc_0': 'Workshop',
|
|
'type_color_0': '#0070C0',
|
|
}
|
|
|
|
schedule = parse_inline_schedule(form_data)
|
|
|
|
# Should only have 1 row (empty row skipped)
|
|
assert len(schedule) == 1
|
|
assert schedule.iloc[0]['Program'] == 'Program 1'
|