diff --git a/frontend/index.html b/frontend/index.html index 661bab9..12224c1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1905,7 +1905,7 @@ // ===== Version ===== // Single source of truth for the PWA version: shown in the header and used // by the server version check in pysimConnect(). -const SIMPLE_VERSION = '3.23.3'; +const SIMPLE_VERSION = '3.23.4'; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; // ===== view history (the browser Back/Forward buttons) ===== @@ -16157,8 +16157,8 @@ function eventFormFieldsHtml(cfg, fields, values, prefix, withOnchange) { html += ''; } else { diff --git a/frontend/sw.js b/frontend/sw.js index 5f41cd3..d7e7bbc 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simple-v416'; +const CACHE = 'simple-v417'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/event_forms.test.js b/frontend/tests/event_forms.test.js index dc40a91..2b56e4a 100644 --- a/frontend/tests/event_forms.test.js +++ b/frontend/tests/event_forms.test.js @@ -38,6 +38,8 @@ eval(extractBlock('const EVENT_INNER_MAX = ', 'function pysimEventSendForm').rep eval(extractFunc(html, 'eventFieldsForRelease')); eval(extractFunc(html, 'encPlmn')); eval(extractFunc(html, 'bytesToHex')); +eval(extractFunc(html, 'esc')); +eval(extractFunc(html, 'eventFormFieldsHtml')); test('CMD_NAMES decodes timer management and the BIP commands', () => { assert.strictEqual(CMD_NAMES['01'], 'REFRESH'); @@ -344,3 +346,32 @@ test('the shared event vectors match the PWA builders', () => { 'scts ' + JSON.stringify(v)); } }); + +test('every event form renders - numeric option values are stringified (v3.23.4)', () => { + // esc() is string-only and most EVENT_FORMS option values are numbers + // (e.g. {v:0,l:'Normal service'} and the ACCESSTECH list): rendering the + // form with esc(o.v) crashed with 'str.replace is not a function' for the + // Phone tab and the test-script step editor alike (v3.22.0 regression). + globalThis.t = s => s; + const bad = []; + for (const [ev, cfg] of Object.entries(EVENT_FORMS)) { + if (!cfg) continue; // the unimplemented events carry a null form + for (const withOnchange of [true, false]) { + try { + eventFormFieldsHtml(cfg, cfg.fields, {}, 'x-', withOnchange); + } catch (e) { + bad.push(ev + (withOnchange ? '/phone' : '/step') + ': ' + e.message); + } + } + } + assert.deepStrictEqual(bad, [], 'every event form must render'); + // a numeric option value lands as its string form and prefills selected + const cfg = EVENT_FORMS[0x00]; + const sel = cfg.fields.find(f => f.type === 'select' && f.opts.some(o => typeof o.v === 'number')); + assert.ok(sel, 'a numeric-option select must exist'); + const num = sel.opts.find(o => typeof o.v === 'number'); + const out = eventFormFieldsHtml(cfg, cfg.fields, {[sel.id]: num.v}, 'x-', true); + assert.ok(out.includes('value="' + String(num.v) + '"'), out); + assert.ok(out.includes('value="' + String(num.v) + '" selected'), out); + delete globalThis.t; +}); diff --git a/pyproject.toml b/pyproject.toml index c5644df..cba09e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-simple-server" -version = "3.23.3" +version = "3.23.4" description = "HTTP REST server wrapping pysim for the SIMple PWA" requires-python = ">=3.8" # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py index e2db4fe..b587fd6 100644 --- a/pysim_simple_server/server.py +++ b/pysim_simple_server/server.py @@ -36,7 +36,7 @@ from osmocom.tlv import BER_TLV_IE from osmocom.utils import rpad -VERSION = '3.23.3' +VERSION = '3.23.4' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE