diff --git a/lib/structs/field_content.ex b/lib/structs/field_content.ex
index 19c101c..a5deb99 100644
--- a/lib/structs/field_content.ex
+++ b/lib/structs/field_content.ex
@@ -55,6 +55,9 @@ defmodule ExPass.Structs.FieldContent do
* "PKNumberStylePercent"
* "PKNumberStyleScientific"
* "PKNumberStyleSpellOut"
+
+ - `value`: The value to use for the field. This can be a localizable string, ISO 8601 date, or number.
+ This field is required. A date or time value must include a time zone.
"""
use TypedStruct
@@ -126,6 +129,18 @@ defmodule ExPass.Structs.FieldContent do
"""
@type number_style() :: String.t()
+ @typedoc """
+ The value to use for the field.
+
+ Required. Can be:
+ - A localizable string (e.g., "Hello, World!")
+ - An ISO 8601 date string (e.g., "2023-04-15T14:30:00Z")
+ - A number (e.g., 42)
+
+ A date or time value must include a time zone.
+ """
+ @type value() :: String.t() | number() | DateTime.t() | Date.t()
+
typedstruct do
field :attributed_value, attributed_value(), default: nil
field :change_message, String.t(), default: nil
@@ -137,6 +152,7 @@ defmodule ExPass.Structs.FieldContent do
field :key, String.t(), enforce: true
field :label, String.t(), default: nil
field :number_style, number_style(), default: nil
+ field :value, value(), enforce: true
end
@doc """
@@ -154,6 +170,7 @@ defmodule ExPass.Structs.FieldContent do
• key
• label
• number_style
+ • value
## Parameters
@@ -169,23 +186,23 @@ defmodule ExPass.Structs.FieldContent do
## Examples
- iex> FieldContent.new(%{key: "field1", attributed_value: "Hello, World!"})
- %FieldContent{key: "field1", attributed_value: "Hello, World!", change_message: nil, currency_code: nil, data_detector_types: nil, date_style: nil, ignores_time_zone: nil, is_relative: nil, label: nil, number_style: nil}
+ iex> FieldContent.new(%{key: "field1", value: "Hello, World!"})
+ %FieldContent{key: "field1", attributed_value: nil, change_message: nil, currency_code: nil, data_detector_types: nil, date_style: nil, ignores_time_zone: nil, is_relative: nil, label: nil, number_style: nil, value: "Hello, World!"}
- iex> FieldContent.new(%{key: "field2", attributed_value: 42, data_detector_types: ["PKDataDetectorTypePhoneNumber"], date_style: "PKDateStyleShort", ignores_time_zone: true, is_relative: false, number_style: "PKNumberStyleDecimal"})
- %FieldContent{key: "field2", attributed_value: 42, change_message: nil, currency_code: nil, data_detector_types: ["PKDataDetectorTypePhoneNumber"], date_style: "PKDateStyleShort", ignores_time_zone: true, is_relative: false, label: nil, number_style: "PKNumberStyleDecimal"}
+ iex> FieldContent.new(%{key: "field2", value: 42, data_detector_types: ["PKDataDetectorTypePhoneNumber"], date_style: "PKDateStyleShort", ignores_time_zone: true, is_relative: false, number_style: "PKNumberStyleDecimal"})
+ %FieldContent{key: "field2", attributed_value: nil, change_message: nil, currency_code: nil, data_detector_types: ["PKDataDetectorTypePhoneNumber"], date_style: "PKDateStyleShort", ignores_time_zone: true, is_relative: false, label: nil, number_style: "PKNumberStyleDecimal", value: 42}
iex> datetime = ~U[2023-04-15 14:30:00Z]
- iex> field_content = FieldContent.new(%{key: "field3", attributed_value: datetime, currency_code: "USD", date_style: "PKDateStyleLong", ignores_time_zone: true, is_relative: true})
- iex> %FieldContent{key: "field3", attributed_value: ^datetime, currency_code: "USD", date_style: "PKDateStyleLong", ignores_time_zone: true, is_relative: true} = field_content
+ iex> field_content = FieldContent.new(%{key: "field3", value: datetime, currency_code: "USD", date_style: "PKDateStyleLong", ignores_time_zone: true, is_relative: true})
+ iex> %FieldContent{key: "field3", value: ^datetime, currency_code: "USD", date_style: "PKDateStyleLong", ignores_time_zone: true, is_relative: true} = field_content
iex> field_content.change_message
nil
- iex> FieldContent.new(%{key: "field4", attributed_value: "Click here", data_detector_types: ["PKDataDetectorTypeLink"], date_style: "PKDateStyleFull", is_relative: false, number_style: "PKNumberStylePercent"})
- %FieldContent{key: "field4", attributed_value: "Click here", change_message: nil, currency_code: nil, data_detector_types: ["PKDataDetectorTypeLink"], date_style: "PKDateStyleFull", ignores_time_zone: nil, is_relative: false, label: nil, number_style: "PKNumberStylePercent"}
+ iex> FieldContent.new(%{key: "field4", value: "Click here", data_detector_types: ["PKDataDetectorTypeLink"], date_style: "PKDateStyleFull", is_relative: false, number_style: "PKNumberStylePercent"})
+ %FieldContent{key: "field4", attributed_value: nil, change_message: nil, currency_code: nil, data_detector_types: ["PKDataDetectorTypeLink"], date_style: "PKDateStyleFull", ignores_time_zone: nil, is_relative: false, label: nil, number_style: "PKNumberStylePercent", value: "Click here"}
- iex> FieldContent.new(%{key: "field5", attributed_value: "No detectors", data_detector_types: [], change_message: "Updated to %@", ignores_time_zone: true, is_relative: true, label: "Field Label", number_style: "PKNumberStyleScientific"})
- %FieldContent{key: "field5", attributed_value: "No detectors", change_message: "Updated to %@", currency_code: nil, data_detector_types: [], date_style: nil, ignores_time_zone: true, is_relative: true, label: "Field Label", number_style: "PKNumberStyleScientific"}
+ iex> FieldContent.new(%{key: "field5", value: "No detectors", data_detector_types: [], change_message: "Updated to %@", ignores_time_zone: true, is_relative: true, label: "Field Label", number_style: "PKNumberStyleScientific"})
+ %FieldContent{key: "field5", attributed_value: nil, change_message: "Updated to %@", currency_code: nil, data_detector_types: [], date_style: nil, ignores_time_zone: true, is_relative: true, label: "Field Label", number_style: "PKNumberStyleScientific", value: "No detectors"}
"""
@spec new(map()) :: %__MODULE__{}
def new(attrs \\ %{}) do
@@ -202,6 +219,7 @@ defmodule ExPass.Structs.FieldContent do
|> validate(:key, &Validators.validate_required_string(&1, :key))
|> validate(:label, &Validators.validate_optional_string(&1, :label))
|> validate(:number_style, &Validators.validate_number_style/1)
+ |> validate(:value, &Validators.validate_required_value(&1, :value))
struct!(__MODULE__, attrs)
end
diff --git a/lib/utils/validators.ex b/lib/utils/validators.ex
index fe7f35a..6c2751e 100644
--- a/lib/utils/validators.ex
+++ b/lib/utils/validators.ex
@@ -422,6 +422,72 @@ defmodule ExPass.Utils.Validators do
def validate_number_style(_), do: {:error, "number_style must be a string"}
+ @doc """
+ Validates a required value field.
+
+ The field must be a non-nil value. The value can be a localizable string, ISO 8601 date, or number.
+
+ ## Parameters
+
+ * `value` - The value to validate. A date or time value must include a time zone.
+ * `field_name` - The name of the field being validated as an atom.
+
+ ## Returns
+
+ * `:ok` if the value is valid (non-nil).
+ * `{:error, reason}` if the value is not valid, where reason is a string explaining the error.
+
+ ## Examples
+
+ iex> validate_required_value(42, :value)
+ :ok
+
+ iex> validate_required_value(nil, :value)
+ {:error, "value is a required field and cannot be nil"}
+
+ iex> validate_required_value("2021-09-15T15:53:00Z", :value)
+ :ok
+
+ iex> validate_required_value("localizable string", :value)
+ :ok
+
+ iex> validate_required_value(nil, :another_field)
+ {:error, "value is a required field and cannot be nil"}
+
+ iex> validate_required_value("2023-04-15T14:30:00", :value)
+ {:error, "Date value must include a time zone"}
+
+ """
+ @spec validate_required_value(String.t() | number() | DateTime.t() | Date.t() | nil, atom()) ::
+ :ok | {:error, String.t()}
+ def validate_required_value(nil, field_name),
+ do: {:error, "#{field_name} is a required field and cannot be nil"}
+
+ def validate_required_value(value, _field_name) when is_binary(value) do
+ if String.contains?(value, "T") and not String.contains?(value, "Z") do
+ {:error, "Date value must include a time zone"}
+ else
+ :ok
+ end
+ end
+
+ def validate_required_value(value, _field_name) when is_number(value), do: :ok
+
+ def validate_required_value(%DateTime{} = value, _field_name) do
+ DateTime.to_iso8601(value)
+
+ :ok
+ end
+
+ def validate_required_value(%Date{} = value, _field_name) do
+ Date.to_iso8601(value)
+
+ :ok
+ end
+
+ def validate_required_value(_value, field_name),
+ do: {:error, "#{field_name} must be a string, number, DateTime, or Date"}
+
defp contains_unsupported_html_tags?(string) do
# Remove all valid anchor tags
string_without_anchors = String.replace(string, ~r{]*>.*?|]*/>}, "")
diff --git a/test/structs/field_content_test.exs b/test/structs/field_content_test.exs
index 345b582..e0531bc 100644
--- a/test/structs/field_content_test.exs
+++ b/test/structs/field_content_test.exs
@@ -13,59 +13,74 @@ defmodule ExPass.Structs.FieldContentTest do
assert_raise ArgumentError,
"The change_message must be a string containing the '%@' placeholder for the new value.",
fn ->
- FieldContent.new(%{key: "test_key", change_message: message})
+ FieldContent.new(%{
+ key: "test_key",
+ value: "test_value",
+ change_message: message
+ })
end
end
test "new/1 creates a FieldContent struct with valid change_message containing '%@' placeholder" do
message = "Balance updated to %@"
- result = FieldContent.new(%{key: "test_key", change_message: message})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", change_message: message})
assert result.change_message == message
assert result.key == "test_key"
+ assert result.value == "test_value"
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("changeMessage":"Balance updated to %@")
end
test "new/1 trims whitespace from change_message while preserving '%@' placeholder" do
message = " Trimmed message %@ "
- result = FieldContent.new(%{key: "test_key", change_message: message})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", change_message: message})
assert result.change_message == "Trimmed message %@"
assert result.key == "test_key"
+ assert result.value == "test_value"
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("changeMessage":"Trimmed message %@")
end
end
describe "attributed_value" do
test "new/1 creates an empty FieldContent struct when no attributes are provided" do
- result = FieldContent.new(%{key: "test_key"})
- assert %FieldContent{attributed_value: nil} = result
+ result = FieldContent.new(%{key: "test_key", value: "test_value"})
+ assert %FieldContent{attributed_value: nil, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
refute encoded =~ "attributedValue"
end
test "new/1 creates a valid FieldContent struct with string" do
input_string = "Hello, World!"
- result = FieldContent.new(%{key: "test_key", attributed_value: input_string})
- assert %FieldContent{attributed_value: ^input_string} = result
+ result =
+ FieldContent.new(%{key: "test_key", value: "test_value", attributed_value: input_string})
+
+ assert %FieldContent{attributed_value: ^input_string, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("attributedValue":"Hello, World!")
end
test "new/1 creates a valid FieldContent struct with number" do
input_number = 42
- result = FieldContent.new(%{key: "test_key", attributed_value: input_number})
- assert %FieldContent{attributed_value: ^input_number} = result
+ result =
+ FieldContent.new(%{key: "test_key", value: "test_value", attributed_value: input_number})
+
+ assert %FieldContent{attributed_value: ^input_number, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("attributedValue":42)
end
@@ -76,28 +91,38 @@ defmodule ExPass.Structs.FieldContentTest do
assert_raise ArgumentError,
"Invalid attributed_value type. Supported types are: String (including tag), number, DateTime and Date",
fn ->
- FieldContent.new(%{key: "test_key", attributed_value: invalid_value})
+ FieldContent.new(%{
+ key: "test_key",
+ value: "test_value",
+ attributed_value: invalid_value
+ })
end
end
end
test "new/1 creates a valid FieldContent struct with DateTime" do
input_time = DateTime.utc_now()
- result = FieldContent.new(%{key: "test_key", attributed_value: input_time})
- assert %FieldContent{attributed_value: ^input_time} = result
+ result =
+ FieldContent.new(%{key: "test_key", value: "test_value", attributed_value: input_time})
+
+ assert %FieldContent{attributed_value: ^input_time, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("attributedValue":"#{DateTime.to_iso8601(input_time)}")
end
test "new/1 creates a valid FieldContent struct with Date" do
input_date = Date.utc_today()
- result = FieldContent.new(%{key: "test_key", attributed_value: input_date})
- assert %FieldContent{attributed_value: ^input_date} = result
+ result =
+ FieldContent.new(%{key: "test_key", value: "test_value", attributed_value: input_date})
+
+ assert %FieldContent{attributed_value: ^input_date, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("attributedValue":"#{Date.to_iso8601(input_date)}")
end
@@ -107,62 +132,72 @@ defmodule ExPass.Structs.FieldContentTest do
assert_raise ArgumentError,
"Supported types are: String (including tag), number, DateTime and Date",
fn ->
- FieldContent.new(%{key: "test_key", attributed_value: input_value})
+ FieldContent.new(%{
+ key: "test_key",
+ value: "test_value",
+ attributed_value: input_value
+ })
end
end
test "new/1 creates a valid FieldContent struct with supported HTML tag" do
input_value = "Link"
- result = FieldContent.new(%{key: "test_key", attributed_value: input_value})
- assert %FieldContent{attributed_value: ^input_value} = result
+ result =
+ FieldContent.new(%{key: "test_key", value: "test_value", attributed_value: input_value})
+
+ assert %FieldContent{attributed_value: ^input_value, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("attributedValue":"Link")
end
end
describe "currency_code" do
test "new/1 creates a valid FieldContent struct with valid currency_code as string" do
- result = FieldContent.new(%{key: "test_key", currency_code: "USD"})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", currency_code: "USD"})
- assert %FieldContent{currency_code: "USD"} = result
+ assert %FieldContent{currency_code: "USD", value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("currencyCode":"USD")
end
test "new/1 creates a valid FieldContent struct with valid currency_code as atom" do
- result = FieldContent.new(%{key: "test_key", currency_code: :USD})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", currency_code: :USD})
- assert %FieldContent{currency_code: :USD} = result
+ assert %FieldContent{currency_code: :USD, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("currencyCode":"USD")
end
test "new/1 raises ArgumentError for invalid currency_code" do
assert_raise ArgumentError, ~r/Invalid currency code INVALID/, fn ->
- FieldContent.new(%{key: "test_key", currency_code: "INVALID"})
+ FieldContent.new(%{key: "test_key", value: "test_value", currency_code: "INVALID"})
end
assert_raise ArgumentError, ~r/Invalid currency code INVALID/, fn ->
- FieldContent.new(%{key: "test_key", currency_code: :INVALID})
+ FieldContent.new(%{key: "test_key", value: "test_value", currency_code: :INVALID})
end
end
test "new/1 raises ArgumentError when currency_code is not a string or atom" do
assert_raise ArgumentError, ~r/Currency code must be a string or atom/, fn ->
- FieldContent.new(%{key: "test_key", currency_code: 123})
+ FieldContent.new(%{key: "test_key", value: "test_value", currency_code: 123})
end
end
test "new/1 trims whitespace from currency_code string" do
- result = FieldContent.new(%{key: "test_key", currency_code: " USD "})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", currency_code: " USD "})
- assert %FieldContent{currency_code: "USD"} = result
+ assert %FieldContent{currency_code: "USD", value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("currencyCode":"USD")
end
end
@@ -172,27 +207,31 @@ defmodule ExPass.Structs.FieldContentTest do
result =
FieldContent.new(%{
key: "test_key",
+ value: "test_value",
data_detector_types: ["PKDataDetectorTypePhoneNumber", "PKDataDetectorTypeLink"]
})
assert %FieldContent{
- data_detector_types: ["PKDataDetectorTypePhoneNumber", "PKDataDetectorTypeLink"]
+ data_detector_types: ["PKDataDetectorTypePhoneNumber", "PKDataDetectorTypeLink"],
+ value: "test_value"
} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~
~s("dataDetectorTypes":["PKDataDetectorTypePhoneNumber","PKDataDetectorTypeLink"])
end
test "new/1 creates a valid FieldContent struct with empty data_detector_types" do
- result = FieldContent.new(%{key: "test_key", data_detector_types: []})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", data_detector_types: []})
- assert %FieldContent{data_detector_types: []} = result
+ assert %FieldContent{data_detector_types: [], value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("dataDetectorTypes":[])
end
@@ -202,6 +241,7 @@ defmodule ExPass.Structs.FieldContentTest do
fn ->
FieldContent.new(%{
key: "test_key",
+ value: "test_value",
data_detector_types: ["InvalidDetector"]
})
end
@@ -211,6 +251,7 @@ defmodule ExPass.Structs.FieldContentTest do
assert_raise ArgumentError, ~r/data_detector_types must be a list/, fn ->
FieldContent.new(%{
key: "test_key",
+ value: "test_value",
data_detector_types: "PKDataDetectorTypePhoneNumber"
})
end
@@ -219,12 +260,14 @@ defmodule ExPass.Structs.FieldContentTest do
describe "date_style" do
test "new/1 creates a valid FieldContent struct with date_style" do
- result = FieldContent.new(%{key: "test_key", date_style: "PKDateStyleShort"})
+ result =
+ FieldContent.new(%{key: "test_key", value: "test_value", date_style: "PKDateStyleShort"})
- assert %FieldContent{date_style: "PKDateStyleShort"} = result
+ assert %FieldContent{date_style: "PKDateStyleShort", value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("dateStyle":"PKDateStyleShort")
end
@@ -238,10 +281,11 @@ defmodule ExPass.Structs.FieldContentTest do
]
for style <- valid_styles do
- result = FieldContent.new(%{key: "test_key", date_style: style})
- assert %FieldContent{date_style: ^style} = result
+ result = FieldContent.new(%{key: "test_key", value: "test_value", date_style: style})
+ assert %FieldContent{date_style: ^style, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("dateStyle":"#{style}")
end
end
@@ -252,6 +296,7 @@ defmodule ExPass.Structs.FieldContentTest do
fn ->
FieldContent.new(%{
key: "test_key",
+ value: "test_value",
date_style: "InvalidStyle"
})
end
@@ -259,183 +304,228 @@ defmodule ExPass.Structs.FieldContentTest do
test "new/1 raises ArgumentError when date_style is not a string" do
assert_raise ArgumentError, ~r/date_style must be a string/, fn ->
- FieldContent.new(%{key: "test_key", date_style: :PKDateStyleShort})
+ FieldContent.new(%{key: "test_key", value: "test_value", date_style: :PKDateStyleShort})
end
end
end
describe "ignores_time_zone" do
test "new/1 creates a valid FieldContent struct with ignores_time_zone set to true" do
- result = FieldContent.new(%{key: "test_key", ignores_time_zone: true})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", ignores_time_zone: true})
- assert %FieldContent{ignores_time_zone: true} = result
+ assert %FieldContent{ignores_time_zone: true, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("ignoresTimeZone":true)
end
test "new/1 creates a valid FieldContent struct with ignores_time_zone set to false" do
- result = FieldContent.new(%{key: "test_key", ignores_time_zone: false})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", ignores_time_zone: false})
- assert %FieldContent{ignores_time_zone: false} = result
+ assert %FieldContent{ignores_time_zone: false, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("ignoresTimeZone":false)
end
test "new/1 defaults to nil when ignores_time_zone is not provided" do
- result = FieldContent.new(%{key: "test_key"})
+ result = FieldContent.new(%{key: "test_key", value: "test_value"})
- assert %FieldContent{ignores_time_zone: nil} = result
+ assert %FieldContent{ignores_time_zone: nil, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
refute encoded =~ "ignoresTimeZone"
end
test "new/1 raises ArgumentError when ignores_time_zone is not a boolean" do
assert_raise ArgumentError, ~r/ignores_time_zone must be a boolean/, fn ->
- FieldContent.new(%{key: "test_key", ignores_time_zone: "true"})
+ FieldContent.new(%{key: "test_key", value: "test_value", ignores_time_zone: "true"})
end
end
end
describe "is_relative" do
test "new/1 creates a valid FieldContent struct with is_relative set to true" do
- result = FieldContent.new(%{key: "test_key", is_relative: true})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", is_relative: true})
- assert %FieldContent{is_relative: true} = result
+ assert %FieldContent{is_relative: true, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("isRelative":true)
end
test "new/1 creates a valid FieldContent struct with is_relative set to false" do
- result = FieldContent.new(%{key: "test_key", is_relative: false})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", is_relative: false})
- assert %FieldContent{is_relative: false} = result
+ assert %FieldContent{is_relative: false, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("isRelative":false)
end
test "new/1 defaults to nil when is_relative is not provided" do
- result = FieldContent.new(%{key: "test_key"})
+ result = FieldContent.new(%{key: "test_key", value: "test_value"})
- assert %FieldContent{is_relative: nil} = result
+ assert %FieldContent{is_relative: nil, value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
refute encoded =~ "isRelative"
end
test "new/1 raises ArgumentError when is_relative is not a boolean" do
assert_raise ArgumentError, ~r/is_relative must be a boolean/, fn ->
- FieldContent.new(%{key: "test_key", is_relative: "true"})
+ FieldContent.new(%{key: "test_key", value: "test_value", is_relative: "true"})
end
end
end
- describe "key" do
- test "new/1 creates a valid FieldContent struct with a key" do
- result = FieldContent.new(%{key: "unique_identifier"})
+ describe "key and value" do
+ test "new/1 creates a valid FieldContent struct with a key and value" do
+ result = FieldContent.new(%{key: "unique_identifier", value: "test_value"})
- assert %FieldContent{key: "unique_identifier"} = result
+ assert %FieldContent{key: "unique_identifier", value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"unique_identifier")
+ assert encoded =~ ~s("value":"test_value")
end
test "new/1 raises ArgumentError when key is not provided" do
assert_raise ArgumentError, "key is a required field and must be a non-empty string", fn ->
- FieldContent.new(%{})
+ FieldContent.new(%{value: "test_value"})
+ end
+ end
+
+ test "new/1 raises ArgumentError when value is not provided" do
+ assert_raise ArgumentError, "value is a required field and cannot be nil", fn ->
+ FieldContent.new(%{key: "unique_identifier"})
end
end
test "new/1 raises ArgumentError when key is an empty string" do
assert_raise ArgumentError, ~r/key cannot be an empty string/, fn ->
- FieldContent.new(%{key: ""})
+ FieldContent.new(%{key: "", value: "test_value"})
end
end
test "new/1 raises ArgumentError when key is not a string" do
assert_raise ArgumentError, "key is a required field and must be a non-empty string", fn ->
- FieldContent.new(%{key: 123})
+ FieldContent.new(%{key: 123, value: "test_value"})
end
end
test "new/1 trims whitespace from key" do
- result = FieldContent.new(%{key: " trimmed_key "})
+ result = FieldContent.new(%{key: " trimmed_key ", value: "test_value"})
- assert %FieldContent{key: "trimmed_key"} = result
+ assert %FieldContent{key: "trimmed_key", value: "test_value"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"trimmed_key")
+ assert encoded =~ ~s("value":"test_value")
+ end
+
+ test "new/1 allows various types for value" do
+ string_value = FieldContent.new(%{key: "string_key", value: "string_value"})
+ assert %FieldContent{key: "string_key", value: "string_value"} = string_value
+
+ number_value = FieldContent.new(%{key: "number_key", value: 42})
+ assert %FieldContent{key: "number_key", value: 42} = number_value
+
+ date_value = FieldContent.new(%{key: "date_key", value: ~D[2023-05-17]})
+ assert %FieldContent{key: "date_key", value: ~D[2023-05-17]} = date_value
+
+ datetime_value = FieldContent.new(%{key: "datetime_key", value: ~U[2023-05-17 10:00:00Z]})
+ assert %FieldContent{key: "datetime_key", value: ~U[2023-05-17 10:00:00Z]} = datetime_value
end
end
describe "label" do
test "new/1 creates a valid FieldContent struct with a label" do
- result = FieldContent.new(%{key: "test_key", label: "Test Label"})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", label: "Test Label"})
- assert %FieldContent{key: "test_key", label: "Test Label"} = result
+ assert %FieldContent{key: "test_key", value: "test_value", label: "Test Label"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("label":"Test Label")
end
test "new/1 creates a valid FieldContent struct without a label" do
- result = FieldContent.new(%{key: "test_key"})
+ result = FieldContent.new(%{key: "test_key", value: "test_value"})
- assert %FieldContent{key: "test_key", label: nil} = result
+ assert %FieldContent{key: "test_key", value: "test_value", label: nil} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
refute encoded =~ "label"
end
test "new/1 trims whitespace from label" do
- result = FieldContent.new(%{key: "test_key", label: " Trimmed Label "})
+ result =
+ FieldContent.new(%{key: "test_key", value: "test_value", label: " Trimmed Label "})
- assert %FieldContent{key: "test_key", label: "Trimmed Label"} = result
+ assert %FieldContent{key: "test_key", value: "test_value", label: "Trimmed Label"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("label":"Trimmed Label")
end
test "new/1 raises ArgumentError when label is not a string" do
assert_raise ArgumentError, ~r/label must be a string/, fn ->
- FieldContent.new(%{key: "test_key", label: 123})
+ FieldContent.new(%{key: "test_key", value: "test_value", label: 123})
end
end
test "new/1 allows an empty string for label" do
- result = FieldContent.new(%{key: "test_key", label: ""})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", label: ""})
- assert %FieldContent{key: "test_key", label: ""} = result
+ assert %FieldContent{key: "test_key", value: "test_value", label: ""} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("label":"")
end
end
describe "number_style" do
test "new/1 creates a valid FieldContent struct with number_style" do
- result = FieldContent.new(%{key: "test_key", number_style: "PKNumberStyleDecimal"})
+ result =
+ FieldContent.new(%{
+ key: "test_key",
+ value: "test_value",
+ number_style: "PKNumberStyleDecimal"
+ })
+
+ assert %FieldContent{
+ key: "test_key",
+ value: "test_value",
+ number_style: "PKNumberStyleDecimal"
+ } = result
- assert %FieldContent{key: "test_key", number_style: "PKNumberStyleDecimal"} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("numberStyle":"PKNumberStyleDecimal")
end
test "new/1 raises ArgumentError for invalid number_style" do
assert_raise ArgumentError, ~r/Invalid number_style/, fn ->
- FieldContent.new(%{key: "test_key", number_style: "InvalidStyle"})
+ FieldContent.new(%{key: "test_key", value: "test_value", number_style: "InvalidStyle"})
end
end
test "new/1 allows nil for number_style" do
- result = FieldContent.new(%{key: "test_key", number_style: nil})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", number_style: nil})
- assert %FieldContent{key: "test_key", number_style: nil} = result
+ assert %FieldContent{key: "test_key", value: "test_value", number_style: nil} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
refute encoded =~ "numberStyle"
end
@@ -448,13 +538,56 @@ defmodule ExPass.Structs.FieldContentTest do
]
Enum.each(styles, fn style ->
- result = FieldContent.new(%{key: "test_key", number_style: style})
+ result = FieldContent.new(%{key: "test_key", value: "test_value", number_style: style})
- assert %FieldContent{key: "test_key", number_style: ^style} = result
+ assert %FieldContent{key: "test_key", value: "test_value", number_style: ^style} = result
encoded = Jason.encode!(result)
assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"test_value")
assert encoded =~ ~s("numberStyle":"#{style}")
end)
end
end
+
+ describe "value" do
+ test "new/1 creates a valid FieldContent struct with a localizable string value" do
+ result = FieldContent.new(%{key: "test_key", value: "Hello, World!"})
+
+ assert %FieldContent{key: "test_key", value: "Hello, World!"} = result
+ encoded = Jason.encode!(result)
+ assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"Hello, World!")
+ end
+
+ test "new/1 creates a valid FieldContent struct with an ISO 8601 date value" do
+ iso_date = "2023-04-15T14:30:00Z"
+ result = FieldContent.new(%{key: "test_key", value: iso_date})
+
+ assert %FieldContent{key: "test_key", value: ^iso_date} = result
+ encoded = Jason.encode!(result)
+ assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":"2023-04-15T14:30:00Z")
+ end
+
+ test "new/1 creates a valid FieldContent struct with a number value" do
+ result = FieldContent.new(%{key: "test_key", value: 42})
+
+ assert %FieldContent{key: "test_key", value: 42} = result
+ encoded = Jason.encode!(result)
+ assert encoded =~ ~s("key":"test_key")
+ assert encoded =~ ~s("value":42)
+ end
+
+ test "new/1 raises ArgumentError for invalid value type" do
+ assert_raise ArgumentError, "value must be a string, number, DateTime, or Date", fn ->
+ FieldContent.new(%{key: "test_key", value: %{invalid: "type"}})
+ end
+ end
+
+ test "new/1 raises ArgumentError for missing time zone in date value" do
+ assert_raise ArgumentError, ~r/Date value must include a time zone/, fn ->
+ FieldContent.new(%{key: "test_key", value: "2023-04-15T14:30:00"})
+ end
+ end
+ end
end