diff --git a/app/templates/views/activity/all-activity.html b/app/templates/views/activity/all-activity.html
index 0d4258f2d..7eb7cb3c5 100644
--- a/app/templates/views/activity/all-activity.html
+++ b/app/templates/views/activity/all-activity.html
@@ -59,13 +59,9 @@
{% endif %}
{% endset %}
-
{% block maincolumn_content %}
All activity
- {% if current_user.has_permissions('manage_templates') and not current_service.all_templates %}
- {% include 'views/dashboard/write-first-messages.html' %}
- {% endif %}
All activity
Sent jobs
@@ -115,6 +111,9 @@
{% endfor %}
{% else %}
+
+ | No batched job messages found (messages are kept for {{ service_data_retention_days }} days). |
+
{% endif %}
diff --git a/app/utils/pagination.py b/app/utils/pagination.py
index 50e6371be..7d3f89918 100644
--- a/app/utils/pagination.py
+++ b/app/utils/pagination.py
@@ -34,17 +34,10 @@ def generate_previous_next_dict(view, service_id, page, title, url_args):
def generate_pagination_pages(total_items, page_size, current_page):
total_pages = (total_items + page_size - 1) // page_size
pagination = {"current": current_page, "pages": [], "last": total_pages}
- if total_pages <= 4:
+ if total_pages <= 9:
pagination["pages"] = list(range(1, total_pages + 1))
else:
- if current_page <= 3:
- pagination["pages"] = [1, 2, 3, total_pages]
- else:
- pagination["pages"] = [
- 1,
- current_page - 1,
- current_page,
- current_page + 1,
- total_pages,
- ]
+ start_page = max(1, min(current_page - 4, total_pages - 8))
+ end_page = min(start_page + 8, total_pages)
+ pagination["pages"] = list(range(start_page, end_page + 1))
return pagination
diff --git a/tests/app/main/views/test_jobs_activity.py b/tests/app/main/views/test_jobs_activity.py
new file mode 100644
index 000000000..8cb171d94
--- /dev/null
+++ b/tests/app/main/views/test_jobs_activity.py
@@ -0,0 +1,179 @@
+from bs4 import BeautifulSoup
+
+from app.utils.pagination import get_page_from_request
+from tests.conftest import SERVICE_ONE_ID
+
+MOCK_JOBS = {
+ "data": [
+ {
+ "archived": False,
+ "created_at": "2024-01-04T20:43:52+00:00",
+ "created_by": {
+ "id": "mocked_user_id",
+ "name": "mocked_user",
+ },
+ "id": "55b242b5-9f62-4271-aff7-039e9c320578",
+ "job_status": "finished",
+ "notification_count": 1,
+ "original_file_name": "mocked_file.csv",
+ "processing_finished": "2024-01-25T23:02:25+00:00",
+ "processing_started": "2024-01-25T23:02:24+00:00",
+ "scheduled_for": None,
+ "service": "21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3",
+ "service_name": {"name": "Mock Texting Service"},
+ "statistics": [{"count": 1, "status": "sending"}],
+ "template": "6a456418-498c-4c86-b0cd-9403c14a216c",
+ "template_name": "Mock Template Name",
+ "template_type": "sms",
+ "template_version": 3,
+ "updated_at": "2024-01-25T23:02:25+00:00",
+ }
+ ],
+ 'links': {
+ 'last': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=3',
+ 'next': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=3',
+ 'prev': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=1'
+ },
+ 'page_size': 50,
+ 'total': 115
+}
+
+
+def test_all_activity(
+ client_request,
+ mocker,
+):
+ current_page = get_page_from_request()
+ mock_get_page_of_jobs = mocker.patch(
+ "app.job_api_client.get_page_of_jobs", return_value=MOCK_JOBS
+ )
+
+ response = client_request.get_response(
+ "main.all_jobs_activity",
+ service_id=SERVICE_ONE_ID,
+ page=current_page,
+ )
+ assert response.status_code == 200, "Request failed"
+ assert response.data is not None, "Response data is None"
+
+ assert "All activity" in response.text
+ mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page)
+ page = BeautifulSoup(response.data, 'html.parser')
+ table = page.find('table')
+ assert table is not None, "Table not found in the response"
+
+ headers = [th.get_text(strip=True) for th in table.find_all('th')]
+ expected_headers = ["Job ID#", "Template", "Time sent", "Sender", "Report"]
+
+ assert headers == expected_headers, f"Expected headers {expected_headers}, but got {headers}"
+
+ rows = table.find('tbody').find_all('tr', class_='table-row')
+ assert len(rows) == 1, "Expected one job row in the table"
+
+ job_row = rows[0]
+ cells = job_row.find_all('td')
+ assert len(cells) == 5, "Expected five columns in the job row"
+
+ job_id_cell = cells[0].find('a').get_text(strip=True)
+
+ assert job_id_cell == "55b242b5", f"Expected job ID '55b242b5', but got '{job_id_cell}'"
+ template_cell = cells[1].get_text(strip=True)
+ assert template_cell == "Mock Template Name", (
+ f"Expected template 'Mock Template Name', but got '{template_cell}'"
+ )
+ time_sent_cell = cells[2].get_text(strip=True)
+ assert time_sent_cell == "01-25-2024 at 06:02 PM", (
+ f"Expected time sent '01-25-2024 at 06:02 PM', but got '{time_sent_cell}'"
+ )
+ sender_cell = cells[3].get_text(strip=True)
+ assert sender_cell == "mocked_user", f"Expected sender 'mocked_user', but got '{sender_cell}'"
+
+ report_cell = cells[4].find('span').get_text(strip=True)
+ assert report_cell == "N/A", f"Expected report 'N/A', but got '{report_cell}'"
+
+ mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page)
+
+
+def test_all_activity_no_jobs(
+ client_request,
+ mocker
+):
+ current_page = get_page_from_request()
+ mock_get_page_of_jobs = mocker.patch(
+ "app.job_api_client.get_page_of_jobs",
+ return_value={
+ "data": [],
+ 'links': {
+ 'last': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=1',
+ 'next': None,
+ 'prev': None
+ },
+ 'page_size': 50,
+ 'total': 0
+ }
+ )
+ response = client_request.get_response(
+ "main.all_jobs_activity",
+ service_id=SERVICE_ONE_ID,
+ page=current_page,
+ )
+
+ assert response.status_code == 200, "Request failed"
+
+ page = BeautifulSoup(response.data, 'html.parser')
+
+ no_jobs_message_td = page.find('td', class_='table-empty-message')
+ assert no_jobs_message_td is not None, "No jobs message not found in the response"
+
+ expected_message = "No batched job messages found (messages are kept for 7 days)."
+ actual_message = no_jobs_message_td.get_text(strip=True)
+
+ assert expected_message == actual_message, (
+ f"Expected message '{expected_message}', but got '{actual_message}'"
+ )
+ mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page)
+
+
+def test_all_activity_pagination(client_request, mocker):
+ current_page = get_page_from_request()
+ mock_get_page_of_jobs = mocker.patch(
+ "app.job_api_client.get_page_of_jobs",
+ return_value={
+ "data": [
+ {
+ "id": f"job-{i}",
+ "created_at": "2024-01-25T23:02:25+00:00",
+ "created_by": {"name": "mocked_user"},
+ "processing_finished": "2024-01-25T23:02:25+00:00",
+ "processing_started": "2024-01-25T23:02:24+00:00",
+ "template_name": "Mock Template Name",
+ "original_file_name": "mocked_file.csv",
+ "notification_count": 1
+ } for i in range(1, 101)
+ ],
+ 'links': {
+ 'last': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=2',
+ 'next': '/service/21b3ee3d-1cb0-4666-bfa0-9c5ac26d3fe3/job?page=2',
+ 'prev': None
+ },
+ 'page_size': 50,
+ 'total': 100
+ }
+ )
+
+ response = client_request.get_response(
+ "main.all_jobs_activity",
+ service_id=SERVICE_ONE_ID,
+ page=current_page,
+ )
+ mock_get_page_of_jobs.assert_called_with(SERVICE_ONE_ID, page=current_page)
+
+ page = BeautifulSoup(response.data, 'html.parser')
+ pagination_controls = page.find_all('li', class_='usa-pagination__item')
+ assert pagination_controls, "Pagination controls not found in the response"
+
+ pagination_texts = [item.get_text(strip=True) for item in pagination_controls]
+ expected_pagination_texts = ['1', '2', 'Next']
+ assert pagination_texts == expected_pagination_texts, (
+ f"Expected pagination controls {expected_pagination_texts}, but got {pagination_texts}"
+ )
diff --git a/tests/app/utils/test_pagination.py b/tests/app/utils/test_pagination.py
index 4a98a1913..a1264d8f5 100644
--- a/tests/app/utils/test_pagination.py
+++ b/tests/app/utils/test_pagination.py
@@ -1,4 +1,10 @@
-from app.utils.pagination import generate_next_dict, generate_previous_dict
+import pytest
+
+from app.utils.pagination import (
+ generate_next_dict,
+ generate_pagination_pages,
+ generate_previous_dict,
+)
def test_generate_previous_dict(client_request):
@@ -20,3 +26,20 @@ def test_generate_previous_next_dict_adds_other_url_args(client_request):
"main.view_notifications", "foo", 2, {"message_type": "blah"}
)
assert "notifications/blah" in result["url"]
+
+
+@pytest.mark.parametrize(
+ ("total_items", "page_size", "current_page", "expected"),
+ [
+ (100, 50, 1, {"current": 1, "pages": [1, 2], "last": 2}),
+ (450, 50, 1, {"current": 1, "pages": [1, 2, 3, 4, 5, 6, 7, 8, 9], "last": 9}),
+ (500, 50, 1, {"current": 1, "pages": [1, 2, 3, 4, 5, 6, 7, 8, 9], "last": 10}),
+ (500, 50, 5, {"current": 5, "pages": [1, 2, 3, 4, 5, 6, 7, 8, 9], "last": 10}),
+ (500, 50, 6, {"current": 6, "pages": [2, 3, 4, 5, 6, 7, 8, 9, 10], "last": 10}),
+ (500, 50, 10, {"current": 10, "pages": [2, 3, 4, 5, 6, 7, 8, 9, 10], "last": 10}),
+ (950, 50, 15, {"current": 15, "pages": [11, 12, 13, 14, 15, 16, 17, 18, 19], "last": 19}),
+ ],
+)
+def test_generate_pagination_pages(total_items, page_size, current_page, expected):
+ result = generate_pagination_pages(total_items, page_size, current_page)
+ assert result == expected