From 94a79f1650b244133d9cf93b78706302418c0d55 Mon Sep 17 00:00:00 2001 From: Mauricio Cassola Date: Mon, 16 Feb 2026 10:15:40 +0100 Subject: [PATCH 1/2] test: add decode/1 tests for non-JSON string inputs Covers the case where raw search text (e.g. "Glon") is passed to decode/1 before a selection is made. Currently crashes with Jason.DecodeError. --- test/live_select/decode_test.exs | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/live_select/decode_test.exs diff --git a/test/live_select/decode_test.exs b/test/live_select/decode_test.exs new file mode 100644 index 0000000..1902ced --- /dev/null +++ b/test/live_select/decode_test.exs @@ -0,0 +1,42 @@ +defmodule LiveSelect.DecodeTest do + @moduledoc false + + use ExUnit.Case, async: true + + describe "decode/1" do + test "decodes nil to empty list" do + assert LiveSelect.decode(nil) == [] + end + + test "decodes empty string to nil" do + assert LiveSelect.decode("") == nil + end + + test "decodes a JSON-encoded map" do + assert LiveSelect.decode(~s({"name":"Berlin","pos":[13.41,52.52]})) == + %{"name" => "Berlin", "pos" => [13.41, 52.52]} + end + + test "decodes a list of JSON-encoded maps" do + assert LiveSelect.decode([ + ~s({"name":"Berlin"}), + ~s({"name":"Rome"}) + ]) == [ + %{"name" => "Berlin"}, + %{"name" => "Rome"} + ] + end + + test "returns plain string as-is when it's not valid JSON" do + assert LiveSelect.decode("Glon") == "Glon" + end + + test "returns string with special characters as-is when not valid JSON" do + assert LiveSelect.decode("São Paulo") == "São Paulo" + end + + test "returns list elements as-is when they are not valid JSON" do + assert LiveSelect.decode(["Berlin", "Rome"]) == ["Berlin", "Rome"] + end + end +end From e32b22fe8b07e1196e65a0b1c65c5dce6df1eb6c Mon Sep 17 00:00:00 2001 From: Mauricio Cassola Date: Mon, 16 Feb 2026 10:17:03 +0100 Subject: [PATCH 2/2] fix: handle non-JSON strings in decode/1 without crashing Rescue decode errors and return the raw value as-is, mirroring how encode/1 already passes simple types through without encoding. --- lib/live_select.ex | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/live_select.ex b/lib/live_select.ex index 4385fcc..f809838 100644 --- a/lib/live_select.ex +++ b/lib/live_select.ex @@ -543,6 +543,14 @@ defmodule LiveSelect do iex> decode(["{\"name\":\"New York City\",\"pos\":[-74.00597,40.71427]}","{\"name\":\"Stockholm\",\"pos\":[18.06871,59.32938]}"]) [%{"name" => "New York City","pos" => [-74.00597,40.71427]}, %{"name" => "Stockholm","pos" => [18.06871,59.32938]}] + + Non-JSON strings (e.g. raw text from a search input) are returned as-is: + + iex> decode("Glon") + "Glon" + + iex> decode(["Berlin", "Rome"]) + ["Berlin", "Rome"] """ def decode(selection) do json = Phoenix.json_library() @@ -550,8 +558,14 @@ defmodule LiveSelect do case selection do nil -> [] "" -> nil - selection when is_list(selection) -> Enum.map(selection, &json.decode!/1) - selection -> json.decode!(selection) + selection when is_list(selection) -> Enum.map(selection, &safe_decode(json, &1)) + selection -> safe_decode(json, selection) end end + + defp safe_decode(json, value) do + json.decode!(value) + rescue + _ -> value + end end