mirror of
https://github.com/netfun2000/hipudding-teslamate.git
synced 2026-02-27 09:44:28 +08:00
* feat: update to Phenix HTML 4.1 * fix: updating to the new Gettext.Backend approach * chore: update pot files ro locale version 45.0.0 * fix: downgrade cloack back to 1.1.2 * revert: merge of mix.lock * fix: new syntax for field name generation for phx-feedback-for * Revert "fix: new syntax for field name generation for phx-feedback-for" This reverts commit 4d95e4f1c9367d2d5c606ced2190ab23edcb971b. * fix: downgrade floki back to 0.35.2 * Revert "fix: downgrade floki back to 0.35.2" This reverts commit a4f0b6bf4b32099d89003c3d20f72333d510e109. * fix: ensure floki nil is handled * style: linter findings * Revert "fix: ensure floki nil is handled" * fix: new syntax for field name generation for phx-feedback-for * debug: floki paramter error * Revert "debug: floki paramter error" This reverts commit 61920267edf5186b593920bde7d87e8b30af55aa. * fix: Remove unnecessary code in settings_test.exs which breaks floki 0.36+
569 lines
19 KiB
Elixir
569 lines
19 KiB
Elixir
defmodule TeslaMateWeb.SettingsLiveTest do
|
|
use TeslaMateWeb.ConnCase
|
|
|
|
alias TeslaMate.{Settings, Locations, Repo}
|
|
|
|
import TestHelper, only: [decimal: 1]
|
|
|
|
describe "units" do
|
|
test "unit of length: shows 'km' by default", %{conn: conn} do
|
|
assert {:ok, view, html} = live(conn, "/settings")
|
|
|
|
assert [
|
|
{"select", _,
|
|
[
|
|
{"option", [{"selected", "selected"}, {"value", "km"}], ["km"]},
|
|
{"option", [{"value", "mi"}], ["mi"]}
|
|
]}
|
|
] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_unit_of_length")
|
|
|
|
assert [
|
|
{"select", _,
|
|
[
|
|
{"option", [{"value", "km"}], ["km"]},
|
|
{"option", [{"selected", "selected"}, {"value", "mi"}], ["mi"]}
|
|
]}
|
|
] =
|
|
render_change(view, :change, %{global_settings: %{unit_of_length: :mi}})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_unit_of_length")
|
|
|
|
assert settings = Settings.get_global_settings!()
|
|
assert settings.unit_of_length == :mi
|
|
end
|
|
|
|
test "unit of temperature: shows '°C' by default", %{conn: conn} do
|
|
assert {:ok, view, html} = live(conn, "/settings")
|
|
|
|
assert [
|
|
{"select", _,
|
|
[
|
|
{"option", [{"selected", "selected"}, {"value", "C"}], ["°C"]},
|
|
{"option", [{"value", "F"}], ["°F"]}
|
|
]}
|
|
] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_unit_of_temperature")
|
|
|
|
assert [
|
|
{"select", _,
|
|
[
|
|
{"option", [{"value", "C"}], ["°C"]},
|
|
{"option", [{"selected", "selected"}, {"value", "F"}], ["°F"]}
|
|
]}
|
|
] =
|
|
render_change(view, :change, %{global_settings: %{unit_of_temperature: :F}})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_unit_of_temperature")
|
|
|
|
assert settings = Settings.get_global_settings!()
|
|
assert settings.unit_of_temperature == :F
|
|
end
|
|
|
|
test "unit of pressure: shows 'bar' by default", %{conn: conn} do
|
|
assert {:ok, view, html} = live(conn, "/settings")
|
|
|
|
assert [
|
|
{"select", _,
|
|
[
|
|
{"option", [{"selected", "selected"}, {"value", "bar"}], ["bar"]},
|
|
{"option", [{"value", "psi"}], ["psi"]}
|
|
]}
|
|
] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_unit_of_pressure")
|
|
|
|
assert [
|
|
{"select", _,
|
|
[
|
|
{"option", [{"value", "bar"}], ["bar"]},
|
|
{"option", [{"selected", "selected"}, {"value", "psi"}], ["psi"]}
|
|
]}
|
|
] =
|
|
render_change(view, :change, %{global_settings: %{unit_of_pressure: :psi}})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_unit_of_pressure")
|
|
|
|
assert settings = Settings.get_global_settings!()
|
|
assert settings.unit_of_pressure == :psi
|
|
end
|
|
end
|
|
|
|
describe "global settings" do
|
|
test "shows :rated by default", %{conn: conn} do
|
|
assert {:ok, _view, html} = live(conn, "/settings")
|
|
|
|
assert [
|
|
{"select", _,
|
|
[
|
|
{"option", [{"value", "ideal"}], ["ideal"]},
|
|
{"option", [{"selected", "selected"}, {"value", "rated"}], ["rated"]}
|
|
]}
|
|
] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_preferred_range")
|
|
end
|
|
|
|
test "changes base_url", %{conn: conn} do
|
|
assert {:ok, view, _html} = live(conn, "/settings")
|
|
|
|
assert render_change(view, :change, %{global_settings: %{base_url: nil}})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_base_url")
|
|
|> Floki.attribute("value") == []
|
|
|
|
assert Settings.get_global_settings!().base_url == nil
|
|
|
|
assert render_change(view, :change, %{
|
|
global_settings: %{base_url: " https://example.com/ "}
|
|
})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_base_url")
|
|
|> Floki.attribute("value") == ["https://example.com"]
|
|
|
|
assert Settings.get_global_settings!().base_url == "https://example.com"
|
|
end
|
|
|
|
test "changes grafana_url", %{conn: conn} do
|
|
assert {:ok, view, _html} = live(conn, "/settings")
|
|
|
|
assert render_change(view, :change, %{global_settings: %{grafana_url: nil}})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_grafana_url")
|
|
|> Floki.attribute("value") == []
|
|
|
|
assert Settings.get_global_settings!().grafana_url == nil
|
|
|
|
assert render_change(view, :change, %{
|
|
global_settings: %{grafana_url: " https://example.com/ "}
|
|
})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_grafana_url")
|
|
|> Floki.attribute("value") == ["https://example.com"]
|
|
|
|
assert Settings.get_global_settings!().grafana_url == "https://example.com"
|
|
end
|
|
end
|
|
|
|
describe "language" do
|
|
alias Locations.Address
|
|
|
|
test "changes language", %{conn: conn} do
|
|
{:ok, %Address{id: address_id}} =
|
|
Locations.create_address(%{
|
|
display_name: "foo",
|
|
name: "bar",
|
|
latitude: 0,
|
|
longitude: 0,
|
|
osm_id: 0,
|
|
osm_type: "way",
|
|
raw: %{}
|
|
})
|
|
|
|
assert {:ok, view, html} = live(conn, "/settings")
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "en"}], ["English"]}] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_language option[selected]")
|
|
|
|
render_change(view, :change, %{global_settings: %{language: "de"}})
|
|
|
|
TestHelper.eventually(fn ->
|
|
assert [{"option", [{"selected", "selected"}, {"value", "de"}], ["German"]}] =
|
|
render(view)
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_language option[selected]")
|
|
|
|
assert %Address{country: "de"} = Repo.get(Address, address_id)
|
|
end)
|
|
end
|
|
|
|
@tag :capture_log
|
|
test "shows error", %{conn: conn} do
|
|
{:ok, %Address{id: address_id}} =
|
|
Locations.create_address(%{
|
|
display_name: "error",
|
|
name: "bar",
|
|
latitude: 0,
|
|
longitude: 0,
|
|
osm_id: 0,
|
|
osm_type: "way",
|
|
raw: %{}
|
|
})
|
|
|
|
assert {:ok, view, html} = live(conn, "/settings")
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "en"}], ["English"]}] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_language option[selected]")
|
|
|
|
render_change(view, :change, %{global_settings: %{language: "de"}})
|
|
|
|
TestHelper.eventually(fn ->
|
|
html = render(view)
|
|
|
|
assert "There was a problem retrieving data from OpenStreetMap. Please try again later." =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("form .field-body")
|
|
|> Floki.find("p.help")
|
|
|> Floki.text()
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "en"}], ["English"]}] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_language option[selected]")
|
|
|
|
assert %Address{
|
|
display_name: "error",
|
|
name: "bar",
|
|
latitude: decimal("0.000000"),
|
|
longitude: decimal("0.000000"),
|
|
osm_id: 0,
|
|
osm_type: "way",
|
|
raw: %{}
|
|
} = Repo.get(Address, address_id)
|
|
end)
|
|
end
|
|
|
|
test "adds a query param when changing the UI language", %{conn: conn} do
|
|
assert {:ok, view, html} = live(conn, "/settings")
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "en"}], ["English"]}] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_ui option[selected]")
|
|
|
|
render_change(view, :change, %{global_settings: %{ui: "de"}})
|
|
assert_redirect(view, path = "/settings?locale=de")
|
|
|
|
assert {:ok, _view, html} = live(conn, path)
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "de"}], ["German"]}] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#global_settings_ui option[selected]")
|
|
end
|
|
end
|
|
|
|
describe "car settings" do
|
|
alias TeslaMate.{Log, Settings}
|
|
|
|
defp car_fixture(attrs) do
|
|
attrs =
|
|
Enum.into(attrs, %{
|
|
efficiency: 0.153,
|
|
eid: 42,
|
|
model: "S",
|
|
vid: 42,
|
|
name: "foo",
|
|
trim_badging: "P100D",
|
|
vin: "12345F",
|
|
settings: %{}
|
|
})
|
|
|
|
{:ok, car} = Log.create_car(attrs)
|
|
|
|
{:ok, _} =
|
|
car
|
|
|> Settings.get_car_settings!()
|
|
|> Settings.update_car_settings(attrs.settings)
|
|
|
|
car
|
|
end
|
|
|
|
test "hides most of the sleep mode settings if streaming is enabled", %{conn: conn} do
|
|
car = car_fixture(settings: %{use_streaming_api: true})
|
|
|
|
assert {:ok, _view, html} = live(conn, "/settings")
|
|
html = Floki.parse_document!(html)
|
|
|
|
assert [] = Floki.find(html, "#car_settings_#{car.id}_suspend_min")
|
|
assert [] = Floki.find(html, "#car_settings_#{car.id}_suspend_after_idle_min")
|
|
|
|
assert [] =
|
|
html
|
|
|> Floki.find("#car_settings_#{car.id}_req_not_unlocked")
|
|
|> Floki.attribute("checked")
|
|
end
|
|
|
|
test "shows 21 and 15 minutes by default if streaming is disabled", %{conn: conn} do
|
|
car = car_fixture(settings: %{use_streaming_api: false})
|
|
|
|
assert {:ok, _view, html} = live(conn, "/settings")
|
|
html = Floki.parse_document!(html)
|
|
|
|
assert [
|
|
{"option", [{"value", "12"}], ["12 min"]},
|
|
{"option", [{"value", "15"}], ["15 min"]},
|
|
{"option", [{"value", "18"}], ["18 min"]},
|
|
{"option", [{"selected", "selected"}, {"value", "21"}], ["21 min"]},
|
|
{"option", [{"value", "24"}], ["24 min"]},
|
|
{"option", [{"value", "27"}], ["27 min"]},
|
|
{"option", [{"value", "30"}], ["30 min"]},
|
|
{"option", [{"value", "35"}], ["35 min"]},
|
|
{"option", [{"value", "40"}], ["40 min"]},
|
|
{"option", [{"value", "45"}], ["45 min"]},
|
|
{"option", [{"value", "50"}], ["50 min"]},
|
|
{"option", [{"value", "55"}], ["55 min"]},
|
|
{"option", [{"value", "60"}], ["60 min"]},
|
|
{"option", [{"value", "65"}], ["65 min"]},
|
|
{"option", [{"value", "70"}], ["70 min"]},
|
|
{"option", [{"value", "75"}], ["75 min"]},
|
|
{"option", [{"value", "80"}], ["80 min"]},
|
|
{"option", [{"value", "85"}], ["85 min"]},
|
|
{"option", [{"value", "90"}], ["90 min"]}
|
|
] = Floki.find(html, "#car_settings_#{car.id}_suspend_min option")
|
|
|
|
assert [
|
|
{"option", [{"value", "3"}], ["3 min"]},
|
|
{"option", [{"value", "5"}], ["5 min"]},
|
|
{"option", [{"value", "10"}], ["10 min"]},
|
|
{"option", [{"selected", "selected"}, {"value", "15"}], ["15 min"]},
|
|
{"option", [{"value", "20"}], ["20 min"]},
|
|
{"option", [{"value", "25"}], ["25 min"]},
|
|
{"option", [{"value", "30"}], ["30 min"]},
|
|
{"option", [{"value", "35"}], ["35 min"]},
|
|
{"option", [{"value", "40"}], ["40 min"]},
|
|
{"option", [{"value", "45"}], ["45 min"]},
|
|
{"option", [{"value", "50"}], ["50 min"]},
|
|
{"option", [{"value", "55"}], ["55 min"]},
|
|
{"option", [{"value", "60"}], ["60 min"]}
|
|
] = Floki.find(html, "#car_settings_#{car.id}_suspend_after_idle_min option")
|
|
end
|
|
|
|
test "By default, the vehicle must be locked to fall asleep", %{conn: conn} do
|
|
car = car_fixture(settings: %{req_not_unlocked: true})
|
|
|
|
assert {:ok, _view, html} = live(conn, "/settings")
|
|
html = Floki.parse_document!(html)
|
|
|
|
assert ["checked"] =
|
|
html
|
|
|> Floki.find("#car_settings_#{car.id}_req_not_unlocked")
|
|
|> Floki.attribute("checked")
|
|
end
|
|
|
|
test "reacts to change events", %{conn: conn} do
|
|
car =
|
|
car_fixture(
|
|
settings: %{
|
|
suspend_min: 21,
|
|
suspend_after_idle_min: 15,
|
|
req_not_unlocked: true,
|
|
free_supercharging: false,
|
|
use_streaming_api: false
|
|
}
|
|
)
|
|
|
|
assert {:ok, view, _html} = live(conn, "/settings")
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "90"}], ["90 min"]}] =
|
|
render_change(view, :change, %{
|
|
"car_settings_#{car.id}" => %{suspend_min: 90, use_streaming_api: false}
|
|
})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#car_settings_#{car.id}_suspend_min option[selected]")
|
|
|
|
assert [settings] = Settings.get_car_settings()
|
|
assert settings.suspend_min == 90
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "30"}], ["30 min"]}] =
|
|
render_change(view, :change, %{
|
|
"car_settings_#{car.id}" => %{
|
|
suspend_after_idle_min: 30,
|
|
use_streaming_api: false
|
|
}
|
|
})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#car_settings_#{car.id}_suspend_after_idle_min option[selected]")
|
|
|
|
assert [settings] = Settings.get_car_settings()
|
|
assert settings.suspend_after_idle_min == 30
|
|
|
|
html =
|
|
render_change(view, :change, %{"car_settings_#{car.id}" => %{req_not_unlocked: false}})
|
|
|> Floki.parse_document!()
|
|
|
|
assert [] =
|
|
html
|
|
|> Floki.find("#car_settings_#{car.id}_req_not_unlocked")
|
|
|> Floki.attribute("checked")
|
|
|
|
assert [settings] = Settings.get_car_settings()
|
|
assert settings.req_not_unlocked == false
|
|
|
|
## Charge cost
|
|
|
|
assert [] ==
|
|
html
|
|
|> Floki.find("#car_settings_#{car.id}_free_supercharging")
|
|
|> Floki.attribute("checked")
|
|
|
|
html =
|
|
render_change(view, :change, %{"car_settings_#{car.id}" => %{free_supercharging: true}})
|
|
|> Floki.parse_document!()
|
|
|
|
assert ["checked"] ==
|
|
html
|
|
|> Floki.find("#car_settings_#{car.id}_free_supercharging")
|
|
|> Floki.attribute("checked")
|
|
|
|
assert [settings] = Settings.get_car_settings()
|
|
assert settings.free_supercharging == true
|
|
|
|
## Streaming API
|
|
|
|
assert [] ==
|
|
html
|
|
|> Floki.find("#car_settings_#{car.id}_use_streaming_api")
|
|
|> Floki.attribute("checked")
|
|
|
|
assert ["checked"] ==
|
|
render_change(view, :change, %{
|
|
"car_settings_#{car.id}" => %{use_streaming_api: true}
|
|
})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#car_settings_#{car.id}_use_streaming_api")
|
|
|> Floki.attribute("checked")
|
|
|
|
assert [settings] = Settings.get_car_settings()
|
|
assert settings.use_streaming_api == true
|
|
end
|
|
|
|
test "changes between cars", %{conn: conn} do
|
|
one = car_fixture(id: 10001, name: "one", eid: 10001, vid: 1001, vin: "10001")
|
|
two = car_fixture(id: 10002, name: "two", eid: 10002, vid: 1002, vin: "10002")
|
|
|
|
assert {:ok, view, html} = live(conn, "/settings")
|
|
|
|
assert one.name ==
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find(".tabs .is-active")
|
|
|> Floki.text()
|
|
|
|
# change settings of car "one"
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "90"}], ["90 min"]}] =
|
|
render_change(view, :change, %{
|
|
"car_settings_#{one.id}" => %{suspend_min: 90, use_streaming_api: false}
|
|
})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#car_settings_#{one.id}_suspend_min option[selected]")
|
|
|
|
assert [settings, _] = Settings.get_car_settings()
|
|
assert settings.suspend_min == 90
|
|
|
|
# change car
|
|
view
|
|
|> element(".tabs li a", two.name)
|
|
|> render_click()
|
|
|
|
assert_redirect(view, path = "/settings?car=#{two.id}")
|
|
assert {:ok, view, html} = live(conn, path)
|
|
|
|
assert two.name ==
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find(".tabs .is-active")
|
|
|> Floki.text()
|
|
|
|
assert [] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#car_settings_#{one.id}_suspend_min option[selected]")
|
|
|
|
# change settings of car "two"
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "60"}], ["60 min"]}] =
|
|
render_change(view, :change, %{
|
|
"car_settings_#{two.id}" => %{suspend_min: 60, use_streaming_api: false}
|
|
})
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#car_settings_#{two.id}_suspend_min option[selected]")
|
|
|
|
# change back
|
|
|
|
view
|
|
|> element(".tabs li a", one.name)
|
|
|> render_click()
|
|
|
|
assert_redirect(view, path = "/settings?car=#{one.id}")
|
|
assert {:ok, _view, html} = live(conn, path)
|
|
|
|
assert one.name ==
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find(".tabs .is-active")
|
|
|> Floki.text()
|
|
|
|
assert [{"option", [{"selected", "selected"}, {"value", "90"}], ["90 min"]}] =
|
|
html
|
|
|> Floki.parse_document!()
|
|
|> Floki.find("#car_settings_#{one.id}_suspend_min option[selected]")
|
|
end
|
|
end
|
|
|
|
describe "updates" do
|
|
alias TeslaMate.Updater
|
|
|
|
import Mock
|
|
|
|
def github_mock do
|
|
release = %{"tag_name" => "v1.1.3", "prerelease" => false, "draft" => false}
|
|
resp = %Tesla.Env{status: 200, body: release}
|
|
{Tesla.Adapter.Finch, [], call: fn _, _ -> {:ok, resp} end}
|
|
end
|
|
|
|
test "informs if an update is available", %{conn: conn} do
|
|
with_mocks [github_mock()] do
|
|
_pid = start_supervised!({Updater, version: "1.0.0", check_after: 0})
|
|
|
|
Process.sleep(1000)
|
|
|
|
assert {:ok, _view, html} = live(conn, "/settings")
|
|
html = Floki.parse_document!(html)
|
|
|
|
assert "#{Application.spec(:teslamate, :vsn)} (Update available: 1.1.3)" ==
|
|
html
|
|
|> Floki.find(".about tr:first-child td")
|
|
|> Floki.text()
|
|
|> String.trim()
|
|
|
|
assert [
|
|
{"a",
|
|
[_, {"href", "https://github.com/teslamate-org/teslamate/releases"}, _, _, _],
|
|
[_, {_, _, ["Update available: 1.1.3"]}]}
|
|
] = Floki.find(html, ".footer a")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "sign-out" do
|
|
import Mock
|
|
|
|
test "tba", %{conn: conn} do
|
|
with_mocks [{TeslaMate.Api, [], signed_in?: fn -> true end, sign_out: fn -> :ok end}] do
|
|
assert {:ok, view, _html} = live(conn, "/settings")
|
|
|
|
view
|
|
|> element("button", "Sign out")
|
|
|> render_click()
|
|
|
|
assert_redirect(view, "/")
|
|
|
|
assert_called(TeslaMate.Api.sign_out())
|
|
end
|
|
end
|
|
end
|
|
end
|