Add test utilities and state machine diagrams for FSM models

- Introduced reusable test utilities in `backend/tests/utils` for FSM transitions, HTMX interactions, and common scenarios.
- Added factory functions for creating test submissions, parks, rides, and photo submissions.
- Implemented assertion helpers for verifying state changes, toast notifications, and transition logs.
- Created comprehensive state machine diagrams for all FSM-enabled models in `docs/STATE_DIAGRAMS.md`, detailing states, transitions, and guard conditions.
This commit is contained in:
pacnpal
2025-12-22 08:55:39 -05:00
parent b508434574
commit 45d97b6e68
71 changed files with 8608 additions and 633 deletions

View File

@@ -0,0 +1,188 @@
{% extends "base/base.html" %}
{% load static %}
{% block title %}Transition History - ThrillWiki Moderation{% endblock %}
{% block extra_css %}
<style>
/* HTMX Loading States */
.htmx-request .htmx-indicator {
opacity: 1;
}
.htmx-request.htmx-indicator {
opacity: 1;
}
.htmx-indicator {
opacity: 0;
transition: opacity 200ms ease-in-out;
}
/* State cloak for Alpine.js */
[x-cloak] {
display: none !important;
}
/* Custom scrollbar for history table */
.overflow-x-auto::-webkit-scrollbar {
height: 8px;
}
.overflow-x-auto::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.overflow-x-auto::-webkit-scrollbar-thumb {
background: rgba(0, 0, 0, 0.2);
border-radius: 4px;
}
.overflow-x-auto::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
.dark .overflow-x-auto::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
}
.dark .overflow-x-auto::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
}
.dark .overflow-x-auto::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
/* Animation for row hover */
tbody tr {
transition: background-color 150ms ease-in-out;
}
/* Skeleton loading animation */
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
</style>
{% endblock %}
{% block content %}
<div class="container max-w-7xl px-4 py-6 mx-auto">
<!-- Page Header -->
<div class="mb-6">
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
<i class="mr-2 text-blue-500 fas fa-history"></i>
Transition History
</h1>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
View all state machine transitions across the system
</p>
</div>
<a href="{% url 'moderation:dashboard' %}"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700">
<i class="mr-2 fas fa-arrow-left"></i>
Back to Dashboard
</a>
</div>
</div>
<!-- Filters -->
{% include 'moderation/partials/history_filters.html' %}
<!-- History Table Container -->
<div id="history-table-wrapper" class="relative">
<div id="history-table-container"
hx-get="{% url 'moderation:moderation-reports-all-history' %}"
hx-trigger="load"
hx-indicator="#page-loading"
hx-swap="outerHTML">
<!-- Initial Loading State -->
<div class="overflow-hidden bg-white border rounded-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
<div class="p-8">
<div class="flex flex-col items-center justify-center">
<div class="w-16 h-16 mb-4 bg-gray-200 rounded-full animate-pulse dark:bg-gray-700"></div>
<div class="w-48 h-4 mb-2 bg-gray-200 rounded animate-pulse dark:bg-gray-700"></div>
<div class="w-32 h-3 bg-gray-200 rounded animate-pulse dark:bg-gray-700"></div>
</div>
</div>
</div>
</div>
<!-- Page Loading Indicator -->
<div id="page-loading" class="absolute inset-0 flex items-center justify-center bg-white/75 dark:bg-gray-800/75 htmx-indicator">
<div class="flex flex-col items-center">
<i class="mb-2 text-3xl text-blue-500 fas fa-spinner fa-spin"></i>
<span class="text-sm text-gray-600 dark:text-gray-400">Loading history...</span>
</div>
</div>
</div>
<!-- History Detail Modal -->
{% include 'moderation/partials/history_detail_modal.html' %}
</div>
{% endblock %}
{% block extra_js %}
<script>
// HTMX Configuration
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
});
// Handle successful history table loads
document.body.addEventListener('htmx:afterSwap', function(evt) {
if (evt.detail.target.id === 'history-table-container') {
console.log('History table updated');
}
});
// Handle errors
document.body.addEventListener('htmx:responseError', function(evt) {
if (evt.detail.target.id === 'history-table-container') {
console.error('Failed to load history:', evt.detail.error);
// Show error message in the container
evt.detail.target.innerHTML = `
<div class="overflow-hidden bg-white border rounded-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
<div class="px-4 py-12 text-center">
<div class="flex flex-col items-center justify-center">
<div class="flex items-center justify-center w-16 h-16 mb-4 text-red-500 bg-red-100 rounded-full dark:bg-red-900/30">
<i class="text-2xl fas fa-exclamation-triangle"></i>
</div>
<h3 class="mb-1 text-sm font-medium text-gray-900 dark:text-gray-300">Failed to load history</h3>
<p class="mb-4 text-sm text-gray-500 dark:text-gray-400">There was an error loading the transition history.</p>
<button class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-500"
hx-get="{% url 'moderation:moderation-reports-all-history' %}"
hx-target="#history-table-container"
hx-swap="outerHTML">
<i class="mr-2 fas fa-sync-alt"></i>
Try Again
</button>
</div>
</div>
</div>
`;
htmx.process(evt.detail.target);
}
});
// History modal event handler
document.addEventListener('open-history-modal', function() {
const modal = document.querySelector('#history-detail-modal');
if (modal && modal.__x) {
modal.__x.$data.open = true;
}
});
</script>
{% endblock %}