Files
potato-mesh/web/spec/helpers/node_helpers_spec.rb
T
l5y 4d38ddd341 web: facelift (#716)
* web: facelift

* web: facelift

* web: facelift

* web: address review comments

* web: address review comments

* web: address review comments

* web: address review comments

* web: address review comments

* web: address review comments

* web: more css magic

* web: link parsing for chat contact

* web: remove one-letter fallback for shortnames

* Potential fix for pull request finding 'CodeQL / Incomplete multi-character sanitization'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* web: fix fallback for shortnames

* web: address review comments

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-04-07 21:38:43 +02:00

211 lines
7.5 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright © 2025-26 l5yth & contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# frozen_string_literal: true
require "spec_helper"
require "rack/utils"
require "json"
RSpec.describe PotatoMesh::App::Helpers do
# Build a lightweight host class that includes the module under test.
let(:harness_class) do
Class.new do
include PotatoMesh::App::Helpers
end
end
subject(:helper) { harness_class.new }
# ---------------------------------------------------------------------------
# node_long_name_link
# ---------------------------------------------------------------------------
describe "#node_long_name_link" do
it "returns an HTML anchor when both long_name and identifier are present" do
html = helper.node_long_name_link("Alpha Node", "!aabbccdd")
expect(html).to include("<a")
expect(html).to include("Alpha Node")
expect(html).to include("!aabbccdd")
end
it "escapes HTML in the long name" do
html = helper.node_long_name_link("<script>", "!aabbccdd")
expect(html).not_to include("<script>")
expect(html).to include("&lt;script&gt;")
end
it "returns just the escaped text when identifier is nil" do
html = helper.node_long_name_link("Plain Name", nil)
expect(html).to eq("Plain Name")
expect(html).not_to include("<a")
end
it "returns an empty string when long_name is nil" do
expect(helper.node_long_name_link(nil, "!aabbccdd")).to eq("")
end
it "returns an empty string when long_name is blank" do
expect(helper.node_long_name_link(" ", "!aabbccdd")).to eq("")
end
it "includes the data-node-id attribute when identifier is present" do
html = helper.node_long_name_link("Alpha", "!aabbccdd")
expect(html).to include("data-node-id=")
end
it "applies custom css_class when provided" do
html = helper.node_long_name_link("Alpha", "!aabbccdd", css_class: "my-class")
expect(html).to include('class="my-class"')
end
it "omits class attribute when css_class is nil" do
html = helper.node_long_name_link("Alpha", "!aabbccdd", css_class: nil)
expect(html).not_to include("class=")
end
end
# ---------------------------------------------------------------------------
# normalize_json_value
# ---------------------------------------------------------------------------
describe "#normalize_json_value" do
it "converts hash symbol keys to strings recursively" do
result = helper.normalize_json_value({ foo: { bar: 1 } })
expect(result).to eq({ "foo" => { "bar" => 1 } })
end
it "normalises elements inside arrays" do
result = helper.normalize_json_value([{ key: "val" }])
expect(result).to eq([{ "key" => "val" }])
end
it "passes nil through unchanged" do
expect(helper.normalize_json_value(nil)).to be_nil
end
it "passes a plain string through unchanged" do
expect(helper.normalize_json_value("hello")).to eq("hello")
end
it "passes integers through unchanged" do
expect(helper.normalize_json_value(42)).to eq(42)
end
it "handles nested hashes with mixed key types" do
result = helper.normalize_json_value({ "a" => { b: 2 } })
expect(result).to eq({ "a" => { "b" => 2 } })
end
end
# ---------------------------------------------------------------------------
# normalize_json_object
# ---------------------------------------------------------------------------
describe "#normalize_json_object" do
it "converts a Hash with symbol keys to string keys" do
result = helper.normalize_json_object({ foo: "bar" })
expect(result).to eq({ "foo" => "bar" })
end
it "parses a valid JSON string into a normalised hash" do
result = helper.normalize_json_object('{"key": "value"}')
expect(result).to eq({ "key" => "value" })
end
it "returns nil for invalid JSON string" do
expect(helper.normalize_json_object("{bad json}")).to be_nil
end
it "returns nil for a blank string" do
expect(helper.normalize_json_object(" ")).to be_nil
end
it "returns nil for nil input" do
expect(helper.normalize_json_object(nil)).to be_nil
end
it "returns nil when parsed JSON is an array (not a hash)" do
expect(helper.normalize_json_object("[1,2,3]")).to be_nil
end
it "returns nil for non-hash, non-string input" do
expect(helper.normalize_json_object(42)).to be_nil
end
end
# ---------------------------------------------------------------------------
# meshcore_companion_display_short_name
# ---------------------------------------------------------------------------
describe "#meshcore_companion_display_short_name" do
it "returns nil for nil input" do
expect(helper.meshcore_companion_display_short_name(nil)).to be_nil
end
it "returns nil for an empty string" do
expect(helper.meshcore_companion_display_short_name("")).to be_nil
end
it "returns nil for a whitespace-only string" do
expect(helper.meshcore_companion_display_short_name(" ")).to be_nil
end
it "returns nil for a single-word name (falls back to raw DB short name)" do
expect(helper.meshcore_companion_display_short_name("Alice")).to be_nil
end
it "returns ' AB ' for a two-word name" do
expect(helper.meshcore_companion_display_short_name("Alice Bob")).to eq(" AB ")
end
it "uses only the first two words for longer names" do
expect(helper.meshcore_companion_display_short_name("Alice Bob Carol")).to eq(" AB ")
end
it "uppercases the initials regardless of original case" do
expect(helper.meshcore_companion_display_short_name("alice bob")).to eq(" AB ")
end
it "strips leading and trailing whitespace before splitting" do
expect(helper.meshcore_companion_display_short_name(" alice bob ")).to eq(" AB ")
end
it "returns the first emoji from the SMP range (U+1F000U+1FFFF)" do
name = "Node \u{1F600}"
expect(helper.meshcore_companion_display_short_name(name)).to eq(" \u{1F600} ")
end
it "returns the first emoji from the misc symbols range (U+2600U+27BF)" do
name = "\u{2600} Sun"
expect(helper.meshcore_companion_display_short_name(name)).to eq(" \u{2600} ")
end
it "returns the first emoji from the arrows range (U+2B00U+2BFF)" do
name = "\u{2B50} Star"
expect(helper.meshcore_companion_display_short_name(name)).to eq(" \u{2B50} ")
end
it "uses the FIRST emoji when multiple are present" do
name = "\u{1F600}\u{1F601} Two"
expect(helper.meshcore_companion_display_short_name(name)).to eq(" \u{1F600} ")
end
it "prefers emoji over initials when both are present" do
name = "Alice \u{1F600} Bob"
expect(helper.meshcore_companion_display_short_name(name)).to eq(" \u{1F600} ")
end
it "returns nil for a single-word name with no emoji (falls back to raw DB short name)" do
expect(helper.meshcore_companion_display_short_name("Zigzag")).to be_nil
end
end
end