From 648900612edb7c0b41ba933d50e13f2368c58d3e Mon Sep 17 00:00:00 2001 From: red person Date: Wed, 3 Jun 2026 08:16:58 +0300 Subject: [PATCH] Ignore non-string calendar date inputs (#1649) --- static/js/calendar/utils.js | 6 ++- tests/test_calendar_utils_dates_js.py | 64 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/test_calendar_utils_dates_js.py diff --git a/static/js/calendar/utils.js b/static/js/calendar/utils.js index 66d30f7..a33cc1c 100644 --- a/static/js/calendar/utils.js +++ b/static/js/calendar/utils.js @@ -118,13 +118,17 @@ export function _ds(d) { } export function _addDays(dateStr, n) { + if (typeof dateStr !== 'string' || !dateStr) return ''; const d = new Date(dateStr + 'T00:00:00'); + if (isNaN(d)) return ''; d.setDate(d.getDate() + n); return _ds(d); } export function _shiftDT(iso, days) { + if (typeof iso !== 'string' || !iso) return ''; const d = new Date(iso); + if (isNaN(d)) return ''; d.setDate(d.getDate() + days); return _ds(d) + (iso.length > 10 ? 'T' + iso.slice(11) : ''); } @@ -147,7 +151,7 @@ export function _tzOffset() { // bucket by the USER's local date. Without this an event at // "2026-05-13T22:00:00Z" (07:00 May 14 JST) would render on May 13. export function _localDateOf(isoStr) { - if (!isoStr) return ''; + if (typeof isoStr !== 'string' || !isoStr) return ''; if (isoStr.length === 10) return isoStr; if (/[Zz]$|[+\-]\d{2}:?\d{2}$/.test(isoStr)) { const d = new Date(isoStr); diff --git a/tests/test_calendar_utils_dates_js.py b/tests/test_calendar_utils_dates_js.py new file mode 100644 index 0000000..23af106 --- /dev/null +++ b/tests/test_calendar_utils_dates_js.py @@ -0,0 +1,64 @@ +import json +import shutil +import subprocess +from pathlib import Path + +import pytest + + +ROOT = Path(__file__).resolve().parents[1] +pytestmark = pytest.mark.skipif(not shutil.which("node"), reason="node binary not on PATH") + + +def _node_eval(source: str): + result = subprocess.run( + ["node", "--input-type=module", "-e", source], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + return json.loads(result.stdout) + + +def test_calendar_date_helpers_ignore_non_string_inputs(): + values = _node_eval( + """ + import { _addDays, _shiftDT, _localDateOf } from './static/js/calendar/utils.js'; + console.log(JSON.stringify({ + addNull: _addDays(null, 1), + addObject: _addDays({bad: true}, 1), + shiftNull: _shiftDT(null, 1), + shiftObject: _shiftDT({bad: true}, 1), + localNull: _localDateOf(null), + localNumber: _localDateOf(123) + })); + """ + ) + + assert values == { + "addNull": "", + "addObject": "", + "shiftNull": "", + "shiftObject": "", + "localNull": "", + "localNumber": "", + } + + +def test_calendar_date_helpers_keep_valid_strings(): + values = _node_eval( + """ + import { _addDays, _shiftDT, _localDateOf } from './static/js/calendar/utils.js'; + console.log(JSON.stringify({ + add: _addDays('2026-06-01', 2), + shift: _shiftDT('2026-06-01T10:30:00', 1), + local: _localDateOf('2026-06-01T23:30:00Z') + })); + """ + ) + + assert values["add"] == "2026-06-03" + assert values["shift"] == "2026-06-02T10:30:00" + assert isinstance(values["local"], str) + assert len(values["local"]) == 10