aboutsummaryrefslogtreecommitdiff
path: root/test/xdg_pipe_menu_renderer_tests.erl
diff options
context:
space:
mode:
authorChristopher R. Nelson <christopher.nelson@languidnights.com>2026-07-21 11:44:41 -0400
committerChristopher R. Nelson <christopher.nelson@languidnights.com>2026-07-21 11:44:41 -0400
commit5ff1fbe175af17fbb808c5ea942f465a932da3af (patch)
treeb6a26b4b7e896e5ed5d85a5003c4d176ed5c8e9e /test/xdg_pipe_menu_renderer_tests.erl
parent7e8472e1e2a0b39746ef86c041d56b92bab5a6b7 (diff)
Rename app from er_xdg_pipe_menu to xdg_pipe_menuHEADmain
Drop the "er-" prefix throughout: module/atom names, filenames, socket paths, and docs. It was an implementation detail, not part of the app's identity. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'test/xdg_pipe_menu_renderer_tests.erl')
-rw-r--r--test/xdg_pipe_menu_renderer_tests.erl167
1 files changed, 167 insertions, 0 deletions
diff --git a/test/xdg_pipe_menu_renderer_tests.erl b/test/xdg_pipe_menu_renderer_tests.erl
new file mode 100644
index 0000000..6d632cf
--- /dev/null
+++ b/test/xdg_pipe_menu_renderer_tests.erl
@@ -0,0 +1,167 @@
+-module(xdg_pipe_menu_renderer_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("xmerl/include/xmerl.hrl").
+-include("xdg_pipe_menu.hrl").
+
+entry(Name, Exec, Icon) ->
+ entry(Name, Exec, Icon, []).
+
+entry(Name, Exec, Icon, Categories) ->
+ entry(Name, Exec, Icon, Categories, false).
+
+entry(Name, Exec, Icon, Categories, Hidden) ->
+ #desktop_entry{
+ name = Name, exec = Exec, icon = Icon, categories = Categories, hidden = Hidden
+ }.
+
+empty_list_renders_empty_menu_test() ->
+ ?assertEqual(
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<openbox_pipe_menu>\n"
+ "</openbox_pipe_menu>\n">>,
+ xdg_pipe_menu_renderer:render([])
+ ).
+
+single_entry_with_icon_test() ->
+ Rendered = xdg_pipe_menu_renderer:render([
+ entry(<<"Firefox">>, <<"firefox">>, <<"firefox">>, [<<"Network">>])
+ ]),
+ ?assertEqual(
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<openbox_pipe_menu>\n"
+ " <menu id=\"Network\" label=\"Network\">\n"
+ " <item label=\"Firefox\" icon=\"firefox\">\n"
+ " <action name=\"Execute\">\n"
+ " <command>firefox</command>\n"
+ " </action>\n"
+ " </item>\n"
+ " </menu>\n"
+ "</openbox_pipe_menu>\n">>,
+ Rendered
+ ).
+
+entry_without_icon_omits_icon_attr_test() ->
+ Rendered = xdg_pipe_menu_renderer:render([
+ entry(<<"Foo">>, <<"foo">>, undefined, [<<"Utility">>])
+ ]),
+ ?assertEqual(
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<openbox_pipe_menu>\n"
+ " <menu id=\"Utility\" label=\"Utility\">\n"
+ " <item label=\"Foo\">\n"
+ " <action name=\"Execute\">\n"
+ " <command>foo</command>\n"
+ " </action>\n"
+ " </item>\n"
+ " </menu>\n"
+ "</openbox_pipe_menu>\n">>,
+ Rendered
+ ).
+
+entries_without_categories_go_in_other_submenu_test() ->
+ Rendered = xdg_pipe_menu_renderer:render([entry(<<"Foo">>, <<"foo">>, undefined)]),
+ ?assertEqual(
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<openbox_pipe_menu>\n"
+ " <menu id=\"Other\" label=\"Other\">\n"
+ " <item label=\"Foo\">\n"
+ " <action name=\"Execute\">\n"
+ " <command>foo</command>\n"
+ " </action>\n"
+ " </item>\n"
+ " </menu>\n"
+ "</openbox_pipe_menu>\n">>,
+ Rendered
+ ).
+
+hidden_entries_are_dropped_test() ->
+ Entries = [
+ entry(<<"Visible">>, <<"visible">>, undefined, [<<"Utility">>]),
+ entry(<<"Invisible">>, <<"invisible">>, undefined, [<<"Utility">>], true)
+ ],
+ Rendered = xdg_pipe_menu_renderer:render(Entries),
+ ?assertNot(binary:match(Rendered, <<"Invisible">>) =/= nomatch),
+ ?assert(binary:match(Rendered, <<"Visible">>) =/= nomatch).
+
+hidden_entry_leaves_no_empty_submenu_test() ->
+ Entries = [entry(<<"Invisible">>, <<"invisible">>, undefined, [<<"Utility">>], true)],
+ Rendered = xdg_pipe_menu_renderer:render(Entries),
+ ?assertNot(binary:match(Rendered, <<"Utility">>) =/= nomatch).
+
+entries_are_sorted_case_insensitively_within_a_submenu_test() ->
+ Entries = [
+ entry(<<"zeta">>, <<"zeta">>, undefined, [<<"Utility">>]),
+ entry(<<"Alpha">>, <<"alpha">>, undefined, [<<"Utility">>]),
+ entry(<<"beta">>, <<"beta">>, undefined, [<<"Utility">>])
+ ],
+ Rendered = xdg_pipe_menu_renderer:render(Entries),
+ Positions = [
+ element(1, binary:match(Rendered, <<"label=\"Alpha\"">>)),
+ element(1, binary:match(Rendered, <<"label=\"beta\"">>)),
+ element(1, binary:match(Rendered, <<"label=\"zeta\"">>))
+ ],
+ ?assertEqual(Positions, lists:sort(Positions)).
+
+submenus_are_sorted_case_insensitively_test() ->
+ Entries = [
+ entry(<<"Zed App">>, <<"zed">>, undefined, [<<"zeta">>]),
+ entry(<<"Alpha App">>, <<"alpha">>, undefined, [<<"Alpha">>]),
+ entry(<<"Beta App">>, <<"beta">>, undefined, [<<"beta">>])
+ ],
+ Rendered = xdg_pipe_menu_renderer:render(Entries),
+ Positions = [
+ element(1, binary:match(Rendered, <<"label=\"Alpha\"">>)),
+ element(1, binary:match(Rendered, <<"label=\"beta\"">>)),
+ element(1, binary:match(Rendered, <<"label=\"zeta\"">>))
+ ],
+ ?assertEqual(Positions, lists:sort(Positions)).
+
+entry_with_multiple_categories_appears_in_each_submenu_test() ->
+ Entries = [entry(<<"Foo">>, <<"foo">>, undefined, [<<"Utility">>, <<"Development">>])],
+ Rendered = xdg_pipe_menu_renderer:render(Entries),
+ {Doc, _Rest} = xmerl_scan:string(binary_to_list(Rendered)),
+ Menus = [E || E = #xmlElement{name = menu} <- Doc#xmlElement.content],
+ ?assertEqual([<<"Development">>, <<"Utility">>], lists:sort([menu_id(M) || M <- Menus])),
+ Items = [I || M <- Menus, I = #xmlElement{name = item} <- M#xmlElement.content],
+ ?assertEqual(2, length(Items)).
+
+escapes_special_characters_test() ->
+ Entry = entry(
+ <<"Foo & <Bar> \"Baz\"">>,
+ <<"foo --title=\"<x>&y\"">>,
+ <<"icon\"&<>">>,
+ [<<"Foo & <Bar>">>]
+ ),
+ Rendered = xdg_pipe_menu_renderer:render([Entry]),
+ ?assertEqual(
+ <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<openbox_pipe_menu>\n"
+ " <menu id=\"Foo &amp; &lt;Bar>\" label=\"Foo &amp; &lt;Bar>\">\n"
+ " <item label=\"Foo &amp; &lt;Bar> &quot;Baz&quot;\" icon=\"icon&quot;&amp;&lt;>\">\n"
+ " <action name=\"Execute\">\n"
+ " <command>foo --title=\"&lt;x&gt;&amp;y\"</command>\n"
+ " </action>\n"
+ " </item>\n"
+ " </menu>\n"
+ "</openbox_pipe_menu>\n">>,
+ Rendered
+ ).
+
+output_is_well_formed_xml_test() ->
+ Entries = [
+ entry(<<"Firefox">>, <<"firefox">>, <<"firefox">>, [<<"Network">>]),
+ entry(<<"Foo & Bar">>, <<"foo">>, undefined, [<<"Network">>])
+ ],
+ Rendered = xdg_pipe_menu_renderer:render(Entries),
+ {Doc, _Rest} = xmerl_scan:string(binary_to_list(Rendered)),
+ ?assertEqual(openbox_pipe_menu, Doc#xmlElement.name),
+ Menus = [E || E = #xmlElement{name = menu} <- Doc#xmlElement.content],
+ ?assertEqual(1, length(Menus)),
+ [Menu] = Menus,
+ Items = [E || E = #xmlElement{name = item} <- Menu#xmlElement.content],
+ ?assertEqual(2, length(Items)).
+
+menu_id(#xmlElement{attributes = Attrs}) ->
+ [Value] = [V || #xmlAttribute{name = id, value = V} <- Attrs],
+ list_to_binary(Value).