feat: add comprehensive tests for Story 2.2 View Exchange List
Add integration tests covering all acceptance criteria: - Dashboard shows list of all exchanges - Each exchange displays name, state, participant count, exchange date - Exchanges sorted by exchange date (upcoming first) - Visual indicator for exchange state - Summary counts for draft, active, completed - Empty state handling Story: 2.2 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
322
tests/integration/test_view_exchange_list.py
Normal file
322
tests/integration/test_view_exchange_list.py
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
"""Integration tests for Story 2.2: View Exchange List."""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from src.models import Exchange
|
||||||
|
|
||||||
|
|
||||||
|
class TestViewExchangeList:
|
||||||
|
"""Test cases for viewing exchange list (Story 2.2)."""
|
||||||
|
|
||||||
|
def test_dashboard_shows_list_of_exchanges(self, client, db, admin): # noqa: ARG002
|
||||||
|
"""Test that dashboard displays all exchanges.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Dashboard shows list of all exchanges
|
||||||
|
"""
|
||||||
|
# Login first
|
||||||
|
client.post(
|
||||||
|
"/admin/login",
|
||||||
|
data={
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"password": "testpassword123",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create multiple exchanges
|
||||||
|
future_close_date = datetime.utcnow() + timedelta(days=7)
|
||||||
|
future_exchange_date = datetime.utcnow() + timedelta(days=14)
|
||||||
|
|
||||||
|
exchange1 = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name="Family Christmas 2025",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=20,
|
||||||
|
registration_close_date=future_close_date,
|
||||||
|
exchange_date=future_exchange_date,
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_REGISTRATION_OPEN,
|
||||||
|
)
|
||||||
|
|
||||||
|
exchange2 = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name="Office Gift Exchange",
|
||||||
|
budget="$15-25",
|
||||||
|
max_participants=15,
|
||||||
|
registration_close_date=future_close_date + timedelta(days=7),
|
||||||
|
exchange_date=future_exchange_date + timedelta(days=7),
|
||||||
|
timezone="America/Chicago",
|
||||||
|
state=Exchange.STATE_DRAFT,
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.add(exchange1)
|
||||||
|
db.session.add(exchange2)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# View dashboard
|
||||||
|
response = client.get("/admin/dashboard")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Verify both exchanges appear
|
||||||
|
assert b"Family Christmas 2025" in response.data
|
||||||
|
assert b"Office Gift Exchange" in response.data
|
||||||
|
|
||||||
|
def test_dashboard_displays_exchange_info(self, client, db, admin): # noqa: ARG002
|
||||||
|
"""Test that each exchange displays required information.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Each exchange displays: name, state, participant count, exchange date
|
||||||
|
"""
|
||||||
|
# Login first
|
||||||
|
client.post(
|
||||||
|
"/admin/login",
|
||||||
|
data={
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"password": "testpassword123",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create an exchange
|
||||||
|
future_close_date = datetime.utcnow() + timedelta(days=7)
|
||||||
|
future_exchange_date = datetime.utcnow() + timedelta(days=14)
|
||||||
|
|
||||||
|
exchange = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name="Test Exchange",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=10,
|
||||||
|
registration_close_date=future_close_date,
|
||||||
|
exchange_date=future_exchange_date,
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_REGISTRATION_OPEN,
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.add(exchange)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# View dashboard
|
||||||
|
response = client.get("/admin/dashboard")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Verify exchange information is displayed
|
||||||
|
assert b"Test Exchange" in response.data
|
||||||
|
assert b"registration_open" in response.data
|
||||||
|
assert b"0 / 10" in response.data # Participant count
|
||||||
|
assert future_exchange_date.strftime("%Y-%m-%d").encode() in response.data
|
||||||
|
|
||||||
|
def test_dashboard_sorted_by_exchange_date(self, client, db, admin): # noqa: ARG002
|
||||||
|
"""Test that exchanges are sorted by exchange date (upcoming first).
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Exchanges sorted by exchange date (upcoming first)
|
||||||
|
"""
|
||||||
|
# Login first
|
||||||
|
client.post(
|
||||||
|
"/admin/login",
|
||||||
|
data={
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"password": "testpassword123",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create exchanges with different dates
|
||||||
|
base_close_date = datetime.utcnow() + timedelta(days=7)
|
||||||
|
|
||||||
|
# Later exchange
|
||||||
|
exchange1 = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name="Later Exchange",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=10,
|
||||||
|
registration_close_date=base_close_date + timedelta(days=30),
|
||||||
|
exchange_date=base_close_date + timedelta(days=60), # Far in future
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_DRAFT,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Earlier exchange
|
||||||
|
exchange2 = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name="Earlier Exchange",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=10,
|
||||||
|
registration_close_date=base_close_date,
|
||||||
|
exchange_date=base_close_date + timedelta(days=7), # Sooner
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_REGISTRATION_OPEN,
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.add(exchange1)
|
||||||
|
db.session.add(exchange2)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# View dashboard
|
||||||
|
response = client.get("/admin/dashboard")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Verify earlier exchange appears before later exchange
|
||||||
|
response_text = response.data.decode()
|
||||||
|
earlier_pos = response_text.index("Earlier Exchange")
|
||||||
|
later_pos = response_text.index("Later Exchange")
|
||||||
|
assert earlier_pos < later_pos
|
||||||
|
|
||||||
|
def test_dashboard_shows_state_indicators(self, client, db, admin): # noqa: ARG002
|
||||||
|
"""Test that visual indicators for exchange state are displayed.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Visual indicator for exchange state
|
||||||
|
"""
|
||||||
|
# Login first
|
||||||
|
client.post(
|
||||||
|
"/admin/login",
|
||||||
|
data={
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"password": "testpassword123",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create exchanges in different states
|
||||||
|
future_close_date = datetime.utcnow() + timedelta(days=7)
|
||||||
|
future_exchange_date = datetime.utcnow() + timedelta(days=14)
|
||||||
|
|
||||||
|
draft_exchange = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name="Draft Exchange",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=10,
|
||||||
|
registration_close_date=future_close_date,
|
||||||
|
exchange_date=future_exchange_date,
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_DRAFT,
|
||||||
|
)
|
||||||
|
|
||||||
|
open_exchange = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name="Open Exchange",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=10,
|
||||||
|
registration_close_date=future_close_date,
|
||||||
|
exchange_date=future_exchange_date,
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_REGISTRATION_OPEN,
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.add(draft_exchange)
|
||||||
|
db.session.add(open_exchange)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# View dashboard
|
||||||
|
response = client.get("/admin/dashboard")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Verify state indicators are present
|
||||||
|
assert b"draft" in response.data
|
||||||
|
assert b"registration_open" in response.data
|
||||||
|
|
||||||
|
def test_dashboard_shows_summary_counts(self, client, db, admin): # noqa: ARG002
|
||||||
|
"""Test that dashboard shows summary counts by state.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Dashboard shows counts for draft, active, and completed exchanges
|
||||||
|
"""
|
||||||
|
# Login first
|
||||||
|
client.post(
|
||||||
|
"/admin/login",
|
||||||
|
data={
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"password": "testpassword123",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create exchanges in different states
|
||||||
|
future_close_date = datetime.utcnow() + timedelta(days=7)
|
||||||
|
future_exchange_date = datetime.utcnow() + timedelta(days=14)
|
||||||
|
|
||||||
|
# 2 draft exchanges
|
||||||
|
for i in range(2):
|
||||||
|
exchange = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name=f"Draft Exchange {i}",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=10,
|
||||||
|
registration_close_date=future_close_date,
|
||||||
|
exchange_date=future_exchange_date,
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_DRAFT,
|
||||||
|
)
|
||||||
|
db.session.add(exchange)
|
||||||
|
|
||||||
|
# 3 active exchanges (registration_open)
|
||||||
|
for i in range(3):
|
||||||
|
exchange = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name=f"Active Exchange {i}",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=10,
|
||||||
|
registration_close_date=future_close_date,
|
||||||
|
exchange_date=future_exchange_date,
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_REGISTRATION_OPEN,
|
||||||
|
)
|
||||||
|
db.session.add(exchange)
|
||||||
|
|
||||||
|
# 1 completed exchange
|
||||||
|
completed_exchange = Exchange(
|
||||||
|
slug=Exchange.generate_slug(),
|
||||||
|
name="Completed Exchange",
|
||||||
|
budget="$20-30",
|
||||||
|
max_participants=10,
|
||||||
|
registration_close_date=future_close_date - timedelta(days=30),
|
||||||
|
exchange_date=future_exchange_date - timedelta(days=20),
|
||||||
|
timezone="America/New_York",
|
||||||
|
state=Exchange.STATE_COMPLETED,
|
||||||
|
)
|
||||||
|
db.session.add(completed_exchange)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
# View dashboard
|
||||||
|
response = client.get("/admin/dashboard")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Verify counts are displayed
|
||||||
|
response_text = response.data.decode()
|
||||||
|
|
||||||
|
# Look for summary section with counts
|
||||||
|
assert "2" in response_text # draft_count
|
||||||
|
assert "3" in response_text # active_count
|
||||||
|
assert "1" in response_text # completed_count
|
||||||
|
|
||||||
|
def test_dashboard_empty_state(self, client, db, admin): # noqa: ARG002
|
||||||
|
"""Test dashboard when no exchanges exist.
|
||||||
|
|
||||||
|
Acceptance Criteria:
|
||||||
|
- Show helpful message when no exchanges exist
|
||||||
|
- Link to create first exchange
|
||||||
|
"""
|
||||||
|
# Login first
|
||||||
|
client.post(
|
||||||
|
"/admin/login",
|
||||||
|
data={
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"password": "testpassword123",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# View dashboard with no exchanges
|
||||||
|
response = client.get("/admin/dashboard")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Verify empty state message
|
||||||
|
assert (
|
||||||
|
b"No exchanges yet" in response.data
|
||||||
|
or b"no exchanges" in response.data.lower()
|
||||||
|
)
|
||||||
|
# Verify link to create exchange
|
||||||
|
assert b"Create" in response.data or b"create" in response.data.lower()
|
||||||
Reference in New Issue
Block a user