prop/test/microwaveprop_web/live/skewt_svg_test.exs

91 lines
3.3 KiB
Elixir

defmodule MicrowavepropWeb.SkewtSvgTest do
use ExUnit.Case, async: true
alias MicrowavepropWeb.SkewtSvg
@profile [
%{"pres" => 1013.0, "hght" => 100.0, "tmpc" => 25.0, "dwpc" => 18.0},
%{"pres" => 925.0, "hght" => 800.0, "tmpc" => 18.0, "dwpc" => 14.0},
%{"pres" => 850.0, "hght" => 1500.0, "tmpc" => 12.0, "dwpc" => 9.0},
%{"pres" => 700.0, "hght" => 3000.0, "tmpc" => 0.0, "dwpc" => -5.0},
%{"pres" => 500.0, "hght" => 5500.0, "tmpc" => -15.0, "dwpc" => -25.0},
%{"pres" => 300.0, "hght" => 9000.0, "tmpc" => -45.0, "dwpc" => -65.0}
]
describe "render/1" do
test "produces a valid <svg> root with the expected viewBox" do
svg = @profile |> SkewtSvg.render() |> IO.iodata_to_binary()
assert svg =~ ~r|<svg[^>]*viewBox="0 0 820 640"|
assert String.ends_with?(svg, "</svg>")
end
test "draws temperature and dewpoint polylines" do
svg = @profile |> SkewtSvg.render() |> IO.iodata_to_binary()
assert svg =~ ~s|class="temp-line"|
assert svg =~ ~s|class="dew-line"|
end
test "tolerates Rust-style atom-keyed levels" do
rust_profile = [
%{pres_mb: 1013.0, hght_m: 100.0, tmpc: 25.0, dwpc: 18.0},
%{pres_mb: 850.0, hght_m: 1500.0, tmpc: 12.0, dwpc: 9.0},
%{pres_mb: 500.0, hght_m: 5500.0, tmpc: -15.0, dwpc: -25.0}
]
svg = rust_profile |> SkewtSvg.render() |> IO.iodata_to_binary()
assert svg =~ ~s|class="temp-line"|
end
test "renders without trace polylines on an empty profile" do
svg = [] |> SkewtSvg.render() |> IO.iodata_to_binary()
# The style block always defines the trace classes; only the
# polyline elements should be missing for an empty profile.
assert svg =~ "<svg"
refute svg =~ ~s|<polyline class="temp-line"|
refute svg =~ ~s|<polyline class="dew-line"|
end
test "skips levels missing the dewpoint" do
partial = [
%{"pres" => 1000.0, "hght" => 100.0, "tmpc" => 20.0, "dwpc" => 10.0},
%{"pres" => 500.0, "hght" => 5000.0, "tmpc" => -10.0, "dwpc" => nil}
]
svg = partial |> SkewtSvg.render() |> IO.iodata_to_binary()
# Temperature line must still draw both points.
assert svg =~ ~s|class="temp-line"|
# Dewpoint line will only contain the bottom point and renders as
# nothing (single-point polyline) — that's fine, the test asserts
# the renderer didn't crash.
assert svg =~ "</svg>"
end
end
describe "coordinate transforms" do
test "pressure_y is monotonically decreasing as pressure decreases" do
y1000 = SkewtSvg.pressure_y(1000)
y500 = SkewtSvg.pressure_y(500)
y100 = SkewtSvg.pressure_y(100)
assert y1000 > y500
assert y500 > y100
end
test "temperature_x at the bottom edge: warmer = further right" do
cold = SkewtSvg.temperature_x(-20, SkewtSvg.pressure_y(1000))
warm = SkewtSvg.temperature_x(20, SkewtSvg.pressure_y(1000))
assert warm > cold
end
test "temperature_x is skewed: same temperature shifts right as pressure decreases" do
bottom = SkewtSvg.temperature_x(0, SkewtSvg.pressure_y(1000))
top = SkewtSvg.temperature_x(0, SkewtSvg.pressure_y(300))
# Skew-T isotherms lean lower-left → upper-right, so the same
# 0 °C plotted at 300 mb sits to the right of 0 °C at 1000 mb.
assert top > bottom
end
end
end