Board row ordering, type-derived row colours, and clearer upcoming events
This commit is contained in:
@@ -669,3 +669,44 @@ def test_a_kind_that_sets_emptytext_still_gets_it():
|
||||
|
||||
def test_base_kind_defaults_to_hiding_when_empty():
|
||||
assert registry.BackupKind.emptytext is None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Timestamp wire format
|
||||
# =============================================================================
|
||||
|
||||
def test_timestamps_are_serialised_as_utc(bk_app, bk_plugin):
|
||||
"""Naive ISO is parsed as BROWSER-LOCAL by JavaScript, silently shifting
|
||||
every timestamp by the viewer's offset before any site-timezone formatting
|
||||
runs. The wire format has to say the value is UTC."""
|
||||
from plugins.backups.models import BackupRevision
|
||||
with bk_app.app_context():
|
||||
result = bk_plugin.apply_collector_payload(
|
||||
_payload(collectedat='2026-08-07T12:00:00Z'))
|
||||
revision = _db.session.get(BackupRevision, result['backuprevisionid'])
|
||||
data = revision.to_dict()
|
||||
assert data['collectedat'].endswith('Z')
|
||||
assert data['createdat'].endswith('Z')
|
||||
# 12:00Z stored naive-UTC, so it round-trips as the same wall clock.
|
||||
assert data['collectedat'].startswith('2026-08-07T12:00:00')
|
||||
|
||||
|
||||
def test_utciso_helper_passes_none_through():
|
||||
from plugins.backups.models.backup import _utciso
|
||||
assert _utciso(None) is None
|
||||
|
||||
|
||||
def test_panel_label_is_rendered_in_the_site_zone(bk_app, bk_plugin):
|
||||
"""The label is baked server-side, so the client cannot correct it later.
|
||||
A UTC afternoon is the morning of the same day at West Jefferson."""
|
||||
from shopdb.core.models import Setting
|
||||
from plugins.backups.models import BackupRevision
|
||||
from plugins.backups.api.routes import _label
|
||||
with bk_app.app_context():
|
||||
_db.session.add(Setting(key='site_timezone', value='America/New_York'))
|
||||
_db.session.commit()
|
||||
result = bk_plugin.apply_collector_payload(
|
||||
_payload(collectedat='2026-08-07T16:30:00Z'))
|
||||
revision = _db.session.get(BackupRevision, result['backuprevisionid'])
|
||||
# 16:30 UTC on 2026-08-07 is 12:30 EDT.
|
||||
assert '12:30' in _label(revision)
|
||||
|
||||
106
tests/test_plugins/test_board_order.py
Normal file
106
tests/test_plugins/test_board_order.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""Tests for notificationtypes.boardorder (row order on the shopfloor board).
|
||||
|
||||
Rows used to run in display-style order and then alphabetically, so what led
|
||||
the screen was an accident of styling and the alphabet. Each type now carries a
|
||||
position, low first, and a shared category row sits wherever its
|
||||
earliest-ordered type puts it.
|
||||
|
||||
The sort itself is frontend (ShopfloorDashboard.vue); pinned here is the
|
||||
contract it sorts on - the feed carries boardorder per card, the type API
|
||||
round-trips and validates it, and the migration seeds the shipped order.
|
||||
"""
|
||||
|
||||
from plugins.notifications.models import Notification, NotificationType
|
||||
|
||||
|
||||
def _make_type(db, typename, boardorder=100, displaystyle='grid', boardcategory=None):
|
||||
t = NotificationType(typename=typename, typecolor='#17a2b8', isactive=True,
|
||||
displaystyle=displaystyle, boardcategory=boardcategory,
|
||||
boardorder=boardorder)
|
||||
db.session.add(t)
|
||||
db.session.commit()
|
||||
return t
|
||||
|
||||
|
||||
def _make_note(db, ntype, text):
|
||||
n = Notification(notificationtypeid=ntype.notificationtypeid,
|
||||
notification=text, businessunitid=None,
|
||||
isactive=True, isshopfloor=True)
|
||||
db.session.add(n)
|
||||
db.session.commit()
|
||||
return n
|
||||
|
||||
|
||||
def _current(client):
|
||||
resp = client.get('/api/notifications/shopfloor')
|
||||
assert resp.status_code == 200, resp.get_json()
|
||||
return resp.get_json()['data']['current']
|
||||
|
||||
|
||||
def test_feed_carries_the_order_per_card(client, db):
|
||||
ntype = _make_type(db, 'Recognition', boardorder=10)
|
||||
_make_note(db, ntype, 'ten years')
|
||||
|
||||
assert _current(client)[0]['boardorder'] == 10
|
||||
|
||||
|
||||
def test_a_type_defaults_to_the_back(client, db):
|
||||
"""A new type lands behind the seeded rows rather than jumping the queue."""
|
||||
ntype = NotificationType(typename='Fresh', typecolor='#17a2b8', isactive=True)
|
||||
db.session.add(ntype)
|
||||
db.session.commit()
|
||||
|
||||
assert ntype.boardorder == 100
|
||||
|
||||
|
||||
def test_api_round_trips_the_order(client, db, auth_headers):
|
||||
create = client.post('/api/notifications/types',
|
||||
json={'typename': 'Awareness', 'boardorder': 30},
|
||||
headers=auth_headers)
|
||||
assert create.status_code in (200, 201), create.get_json()
|
||||
typeid = create.get_json()['data']['notificationtypeid']
|
||||
assert create.get_json()['data']['boardorder'] == 30
|
||||
|
||||
updated = client.put(f'/api/notifications/types/{typeid}',
|
||||
json={'boardorder': 40}, headers=auth_headers)
|
||||
assert updated.get_json()['data']['boardorder'] == 40
|
||||
|
||||
|
||||
def test_api_rejects_a_nonsense_order(client, db, auth_headers):
|
||||
resp = client.post('/api/notifications/types',
|
||||
json={'typename': 'Bad', 'boardorder': 'first'},
|
||||
headers=auth_headers)
|
||||
|
||||
assert resp.status_code == 400
|
||||
assert 'boardorder' in resp.get_data(as_text=True)
|
||||
|
||||
|
||||
def test_api_rejects_an_out_of_range_order(client, db, auth_headers):
|
||||
resp = client.post('/api/notifications/types',
|
||||
json={'typename': 'Bad', 'boardorder': 1000},
|
||||
headers=auth_headers)
|
||||
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_order_change_moves_the_config_version(client, db, auth_headers):
|
||||
"""Open kiosks reload on a layout change; a re-ordered board is one."""
|
||||
ntype = _make_type(db, 'Awareness')
|
||||
before = client.get('/api/notifications/shopfloor').get_json()['data']['configversion']
|
||||
|
||||
client.put(f'/api/notifications/types/{ntype.notificationtypeid}',
|
||||
json={'boardorder': 30}, headers=auth_headers)
|
||||
|
||||
after = client.get('/api/notifications/shopfloor').get_json()['data']['configversion']
|
||||
assert before != after
|
||||
|
||||
|
||||
def test_types_in_one_category_can_carry_different_orders(client, db):
|
||||
"""The row takes the lowest; the feed still reports each type's own."""
|
||||
change = _make_type(db, 'Change', boardorder=30, boardcategory='Current Events')
|
||||
incident = _make_type(db, 'Incident', boardorder=50, boardcategory='Current Events')
|
||||
_make_note(db, change, 'retooling')
|
||||
_make_note(db, incident, 'press down')
|
||||
|
||||
orders = {c['typename']: c['boardorder'] for c in _current(client)}
|
||||
assert orders == {'Change': 30, 'Incident': 50}
|
||||
Reference in New Issue
Block a user