v3.19.0 (cont.): live fixtures; menu text match is case-insensitive by default

No test scripts have been authored yet, so the format is still free to
change - the menu-select text match is now case-insensitive by default
(menu labels are UI text; case_sensitive: true still forces an exact-case
match, and a miss on it names the case-insensitive candidates).

Verification against the live card's own bytes (captured 2026-10-04):
- the Annex A Variant 2 Cyrillic menu (ids 128..130) resolves by text and
  the case behaviour above is pinned ('кофе' -> item 130);
- the applet's SEND SHORT MESSAGE (alpha 'Отправка...', SMS-SUBMIT to
  +79332505884) pins the new alpha and sms checks and the run-log line.

docs/help/api updated; sw cache simple-v388 (help pages changed).
Tests: 819 frontend + 726 Python.
This commit is contained in:
2026-10-04 21:05:19 +03:00
parent 92506e72b9
commit 7250dbd63a
8 changed files with 105 additions and 13 deletions
+16 -4
View File
@@ -6033,22 +6033,34 @@ def _test_run_scp80(server, step, ctx):
def _menu_item_by_text(server, p):
"""Resolve a menu-select step's text against the cached SET UP MENU items
(the item ids vary with the applet's install parameters, so a script
matches the text the card actually shows). Raises a ScriptError naming
matches the text the card actually shows; the match is case-insensitive
unless the step sets `case_sensitive: true`). Raises a ScriptError naming
the available items when nothing - or more than one item - matches."""
menu = getattr(server, 'sim_menu', None) or {}
items = menu.get('items') or []
spec = {'mode': p.get('mode', 'exact'), 'value': p['text'],
'case_sensitive': p.get('case_sensitive', True)}
'case_sensitive': p.get('case_sensitive', False)}
matches = [it for it in items
if testscript.match_text(spec, it.get('text') or '')]
if not matches:
listing = ', '.join('%s=%r' % (it.get('id'), it.get('text'))
for it in items)
hint = ''
if spec.get('case_sensitive') and items:
# A strict-case miss is a common authoring slip with non-ASCII
# menus: name the items a case-insensitive match would find.
loose = [it for it in items if testscript.match_text(
dict(spec, case_sensitive=False), it.get('text') or '')]
if loose:
hint = (' - a case-insensitive match exists (%s): set '
'"case_sensitive": false or fix the case'
% ', '.join('%s=%r' % (it.get('id'), it.get('text'))
for it in loose))
raise testscript.ScriptError(
'menu-select: no menu item matches %r (%s)'
'menu-select: no menu item matches %r (%s)%s'
% (p['text'], 'menu: ' + listing if listing
else 'no menu cached - fetch the menu (a SET UP MENU '
'expectation) first'))
'expectation) first', hint))
if len(matches) > 1:
raise testscript.ScriptError(
'menu-select: %d items match %r (%s) - use the item id'
+4 -1
View File
@@ -281,7 +281,10 @@ def _normalise_params(kind, p):
if mode not in ('exact', 'contains'):
raise ScriptError('menu-select: mode must be exact or contains')
out['mode'] = mode
out['case_sensitive'] = bool(p.get('case_sensitive', True))
# Menu labels are UI text: case-insensitive by default (no test
# scripts predate this choice); `case_sensitive: true` forces an
# exact-case match.
out['case_sensitive'] = bool(p.get('case_sensitive', False))
if not out:
raise ScriptError('menu-select: item_id or text is required')
if 'item_id' in out and 'text' in out: