aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Berbiche <nic.berbiche@gmail.com>2020-07-29 16:29:51 -0400
committerRobert Helgesson <robert@rycee.net>2020-08-14 00:20:49 +0200
commitf4f9f1a618eb7c19b765b02c235e13657b5aa150 (patch)
tree5843f7504ea42f568ecf3cd11d743a2f3b191a8c
parent2a54c353a9ee4c06866047b0abf0a418ab91a475 (diff)
waybar: add module
PR #1329
-rw-r--r--.github/CODEOWNERS3
-rw-r--r--modules/lib/maintainers.nix10
-rw-r--r--modules/misc/news.nix8
-rw-r--r--modules/modules.nix1
-rw-r--r--modules/programs/waybar.nix363
-rw-r--r--tests/default.nix1
-rw-r--r--tests/modules/programs/waybar/broken-settings.nix80
-rw-r--r--tests/modules/programs/waybar/default.nix8
-rw-r--r--tests/modules/programs/waybar/settings-complex-expected.json46
-rw-r--r--tests/modules/programs/waybar/settings-complex.nix59
-rw-r--r--tests/modules/programs/waybar/styling-expected.css23
-rw-r--r--tests/modules/programs/waybar/styling.nix46
-rw-r--r--tests/modules/programs/waybar/systemd-with-graphical-session-target.nix24
-rw-r--r--tests/modules/programs/waybar/systemd-with-graphical-session-target.service16
14 files changed, 688 insertions, 0 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index ee1781ea2c6..3f37aec0d6c 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -103,6 +103,9 @@
/modules/programs/texlive.nix @rycee
+/modules/programs/waybar.nix @berbiche
+/tests/modules/programs/waybar @berbiche
+
/modules/programs/z-lua.nix @marsam
/modules/programs/zathura.nix @rprospero
diff --git a/modules/lib/maintainers.nix b/modules/lib/maintainers.nix
index 49d14ef0638..ff046360071 100644
--- a/modules/lib/maintainers.nix
+++ b/modules/lib/maintainers.nix
@@ -25,4 +25,14 @@
github = "cwyc";
githubId = 16950437;
};
+ berbiche = {
+ name = "Nicolas Berbiche";
+ email = "berbiche@users.noreply.github.com";
+ github = "berbiche";
+ githubId = 20448408;
+ keys = [{
+ longkeyid = "rsa4096/0xB461292445C6E696";
+ fingerprint = "D446 E58D 87A0 31C7 EC15 88D7 B461 2924 45C6 E696";
+ }];
+ };
}
diff --git a/modules/misc/news.nix b/modules/misc/news.nix
index 62f57a44cec..4ec6fc2d6dd 100644
--- a/modules/misc/news.nix
+++ b/modules/misc/news.nix
@@ -1619,6 +1619,14 @@ in
A new module is available: 'services.dropbox'.
'';
}
+
+ {
+ time = "2020-08-13T22:15:27+00:00";
+ condition = hostPlatform.isLinux;
+ message = ''
+ A new module is available: 'programs.waybar'
+ '';
+ }
];
};
}
diff --git a/modules/modules.nix b/modules/modules.nix
index 76ff5aaa8c0..41877c5e21a 100644
--- a/modules/modules.nix
+++ b/modules/modules.nix
@@ -118,6 +118,7 @@ let
(loadModule ./programs/vim.nix { })
(loadModule ./programs/vscode.nix { })
(loadModule ./programs/vscode/haskell.nix { })
+ (loadModule ./programs/waybar.nix { condition = hostPlatform.isLinux; })
(loadModule ./programs/z-lua.nix { })
(loadModule ./programs/zathura.nix { })
(loadModule ./programs/zoxide.nix { })
diff --git a/modules/programs/waybar.nix b/modules/programs/waybar.nix
new file mode 100644
index 00000000000..369f6e32aba
--- /dev/null
+++ b/modules/programs/waybar.nix
@@ -0,0 +1,363 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+ cfg = config.programs.waybar;
+
+ # Used when generating warnings
+ modulesPath = "programs.waybar.settings.[].modules";
+
+ # Taken from <https://github.com/Alexays/Waybar/blob/adaf84304865e143e4e83984aaea6f6a7c9d4d96/src/factory.cpp>
+ defaultModuleNames = [
+ "sway/mode"
+ "sway/workspaces"
+ "sway/window"
+ "wlr/taskbar"
+ "idle_inhibitor"
+ "memory"
+ "cpu"
+ "clock"
+ "disk"
+ "tray"
+ "network"
+ "backlight"
+ "pulseaudio"
+ "mpd"
+ "temperature"
+ "bluetooth"
+ "battery"
+ ];
+
+ isValidCustomModuleName = x:
+ elem x defaultModuleNames || (hasPrefix "custom/" x && stringLength x > 7);
+
+ margins = let
+ mkMargin = name: {
+ "margin-${name}" = mkOption {
+ type = types.nullOr types.int;
+ default = null;
+ example = 10;
+ description = "Margins value without unit.";
+ };
+ };
+ margins = map mkMargin [ "top" "left" "bottom" "right" ];
+ in foldl' mergeAttrs { } margins;
+
+ waybarBarConfig = with lib.types;
+ submodule {
+ options = {
+ layer = mkOption {
+ type = nullOr (enum [ "top" "bottom" ]);
+ default = null;
+ description = ''
+ Decide if the bar is displayed in front (<code>"top"</code>)
+ of the windows or behind (<code>"bottom"</code>).
+ '';
+ example = "top";
+ };
+
+ output = mkOption {
+ type = nullOr (either str (listOf str));
+ default = null;
+ example = literalExample ''
+ [ "DP-1" "!DP-2" "!DP-3" ]
+ '';
+ description = ''
+ Specifies on which screen this bar will be displayed.
+ Exclamation mark(!) can be used to exclude specific output.
+ '';
+ };
+
+ position = mkOption {
+ type = nullOr (enum [ "top" "bottom" "left" "right" ]);
+ default = null;
+ example = "right";
+ description = "Bar position relative to the output.";
+ };
+
+ height = mkOption {
+ type = nullOr ints.unsigned;
+ default = null;
+ example = 5;
+ description =
+ "Height to be used by the bar if possible. Leave blank for a dynamic value.";
+ };
+
+ width = mkOption {
+ type = nullOr ints.unsigned;
+ default = null;
+ example = 5;
+ description =
+ "Width to be used by the bar if possible. Leave blank for a dynamic value.";
+ };
+
+ modules-left = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed on the left.";
+ example = literalExample ''
+ [ "sway/workspaces" "sway/mode" "wlr/taskbar" ]
+ '';
+ };
+
+ modules-center = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed in the center.";
+ example = literalExample ''
+ [ "sway/window" ]
+ '';
+ };
+
+ modules-right = mkOption {
+ type = nullOr (listOf str);
+ default = null;
+ description = "Modules that will be displayed on the right.";
+ example = literalExample ''
+ [ "mpd" "custom/mymodule#with-css-id" "temperature" ]
+ '';
+ };
+
+ modules = mkOption {
+ type = attrsOf unspecified;
+ default = { };
+ description = "Modules configuration.";
+ example = literalExample ''
+ {
+ "sway/window" = {
+ max-length = 50;
+ };
+ "clock" = {
+ format-alt = "{:%a, %d. %b %H:%M}";
+ };
+ }
+ '';
+ };
+
+ margin = mkOption {
+ type = nullOr str;
+ default = null;
+ description = "Margins value using the CSS format without units.";
+ example = "20 5";
+ };
+
+ inherit (margins) margin-top margin-left margin-bottom margin-right;
+
+ name = mkOption {
+ type = nullOr str;
+ default = null;
+ description =
+ "Optional name added as a CSS class, for styling multiple waybars.";
+ example = "waybar-1";
+ };
+
+ gtk-layer-shell = mkOption {
+ type = nullOr bool;
+ default = null;
+ example = false;
+ description =
+ "Option to disable the use of gtk-layer-shell for popups.";
+ };
+ };
+ };
+in {
+ meta.maintainers = [ hm.maintainers.berbiche ];
+
+ options.programs.waybar = with lib.types; {
+ enable = mkEnableOption "Waybar";
+
+ package = mkOption {
+ type = package;
+ default = pkgs.waybar;
+ defaultText = literalExample "${pkgs.waybar}";
+ description = ''
+ Waybar package to use. Set to <code>null</code> to use the default module.
+ '';
+ };
+
+ settings = mkOption {
+ type = listOf waybarBarConfig;
+ default = [ ];
+ description = ''
+ Configuration for Waybar, see <link
+ xlink:href="https://github.com/Alexays/Waybar/wiki/Configuration"/>
+ for supported values.
+ '';
+ example = literalExample ''
+ [
+ {
+ layer = "top";
+ position = "top";
+ height = 30;
+ output = [
+ "eDP-1"
+ "HDMI-A-1"
+ ];
+ modules-left = [ "sway/workspaces" "sway/mode" "wlr/taskbar" ];
+ modules-center = [ "sway/window" "custom/hello-from-waybar" ];
+ modules-right = [ "mpd" "custom/mymodule#with-css-id" "temperature" ];
+ modules = {
+ "sway/workspaces" = {
+ disable-scroll = true;
+ all-outputs = true;
+ };
+ "custom/hello-from-waybar" = {
+ format = "hello {}";
+ max-length = 40;
+ interval = "once";
+ exec = pkgs.writeShellScript "hello-from-waybar" '''
+ echo "from within waybar"
+ ''';
+ };
+ };
+ }
+ ]
+ '';
+ };
+
+ systemd.enable = mkEnableOption "Waybar systemd integration";
+
+ style = mkOption {
+ type = nullOr str;
+ default = null;
+ description = ''
+ CSS style of the bar.
+ See <link xlink:href="https://github.com/Alexays/Waybar/wiki/Configuration"/>
+ for the documentation.
+ '';
+ example = ''
+ * {
+ border: none;
+ border-radius: 0;
+ font-family: Source Code Pro;
+ }
+ window#waybar {
+ background: #16191C;
+ color: #AAB2BF;
+ }
+ #workspaces button {
+ padding: 0 5px;
+ }
+ '';
+ };
+ };
+
+ config = let
+ # Inspired by https://github.com/NixOS/nixpkgs/pull/89781
+ writePrettyJSON = name: x:
+ pkgs.runCommandLocal name { } ''
+ ${pkgs.jq}/bin/jq . > $out <<<${escapeShellArg (builtins.toJSON x)}
+ '';
+
+ configSource = let
+ # Removes nulls because Waybar ignores them for most values
+ removeNulls = filterAttrs (_: v: v != null);
+
+ # Makes the actual valid configuration Waybar accepts
+ # (strips our custom settings before converting to JSON)
+ makeConfiguration = configuration:
+ let
+ # The "modules" option is not valid in the JSON
+ # as its descendants have to live at the top-level
+ settingsWithoutModules =
+ filterAttrs (n: _: n != "modules") configuration;
+ settingsModules =
+ optionalAttrs (configuration.modules != { }) configuration.modules;
+ in removeNulls (settingsWithoutModules // settingsModules);
+ # The clean list of configurations
+ finalConfiguration = map makeConfiguration cfg.settings;
+ in writePrettyJSON "waybar-config.json" finalConfiguration;
+
+ warnings = let
+ mkUnreferencedModuleWarning = name:
+ "The module '${name}' defined in '${modulesPath}' is not referenced "
+ + "in either `modules-left`, `modules-center` or `modules-right` of Waybar's options";
+ mkUndefinedModuleWarning = settings: name:
+ let
+ # Locations where the module is undefined (a combination modules-{left,center,right})
+ locations = flip filter [ "left" "center" "right" ]
+ (x: elem name settings."modules-${x}");
+ mkPath = loc: "'${modulesPath}-${loc}'";
+ # The modules-{left,center,right} configuration that includes
+ # an undefined module
+ path = concatMapStringsSep " and " mkPath locations;
+ in "The module '${name}' defined in ${path} is neither "
+ + "a default module or a custom module declared in '${modulesPath}'";
+ mkInvalidModuleNameWarning = name:
+ "The custom module '${name}' defined in '${modulesPath}' is not a valid "
+ + "module name. A custom module's name must start with 'custom/' "
+ + "like 'custom/mymodule' for instance";
+
+ # Find all modules in `modules-{left,center,right}` and `modules` not declared/referenced.
+ # `cfg.settings` is a list of Waybar configurations
+ # and we need to preserve the index for appropriate warnings
+ allFaultyModules = flip map cfg.settings (settings:
+ let
+ allModules = unique
+ (concatMap (x: attrByPath [ "modules-${x}" ] [ ] settings) [
+ "left"
+ "center"
+ "right"
+ ]);
+ declaredModules = attrNames settings.modules;
+ # Modules declared in `modules` but not referenced in `modules-{left,center,right}`
+ unreferencedModules = subtractLists allModules declaredModules;
+ # Modules listed in modules-{left,center,right} that are not default modules
+ nonDefaultModules = subtractLists defaultModuleNames allModules;
+ # Modules referenced in `modules-{left,center,right}` but not declared in `modules`
+ undefinedModules = subtractLists declaredModules nonDefaultModules;
+ # Check for invalid module names
+ invalidModuleNames =
+ filter (m: !isValidCustomModuleName m) (attrNames settings.modules);
+ in {
+ # The Waybar bar configuration (since config.settings is a list)
+ settings = settings;
+ undef = undefinedModules;
+ unref = unreferencedModules;
+ invalidName = invalidModuleNames;
+ });
+
+ allWarnings = flip concatMap allFaultyModules
+ ({ settings, undef, unref, invalidName }:
+ let
+ unreferenced = map mkUnreferencedModuleWarning unref;
+ undefined = map (mkUndefinedModuleWarning settings) undef;
+ invalid = map mkInvalidModuleNameWarning invalidName;
+ in undefined ++ unreferenced ++ invalid);
+ in allWarnings;
+
+ in mkIf cfg.enable (mkMerge [
+ { home.packages = [ cfg.package ]; }
+ (mkIf (cfg.settings != [ ]) {
+ # Generate warnings about defined but unreferenced modules
+ inherit warnings;
+
+ xdg.configFile."waybar/config".source = configSource;
+ })
+ (mkIf (cfg.style != null) {
+ xdg.configFile."waybar/style.css".text = cfg.style;
+ })
+ (mkIf cfg.systemd.enable {
+ systemd.user.services.waybar = {
+ Unit = {
+ Description =
+ "Highly customizable Wayland bar for Sway and Wlroots based compositors.";
+ Documentation = "https://github.com/Alexays/Waybar/wiki";
+ PartOf = [ "graphical-session.target" ];
+ Requisite = [ "dbus.service" ];
+ After = [ "dbus.service" ];
+ };
+
+ Service = {
+ Type = "dbus";
+ BusName = "fr.arouillard.waybar";
+ ExecStart = "${cfg.package}/bin/waybar";
+ Restart = "always";
+ RestartSec = "1sec";
+ };
+
+ Install = { WantedBy = [ "graphical-session.target" ]; };
+ };
+ })
+ ]);
+}
diff --git a/tests/default.nix b/tests/default.nix
index 53c221bcf69..939a50e2d83 100644
--- a/tests/default.nix
+++ b/tests/default.nix
@@ -82,6 +82,7 @@ import nmt {
./modules/programs/getmail
./modules/services/lieer
./modules/programs/rofi
+ ./modules/programs/waybar
./modules/services/polybar
./modules/services/sxhkd
./modules/services/fluidsynth
diff --git a/tests/modules/programs/waybar/broken-settings.nix b/tests/modules/programs/waybar/broken-settings.nix
new file mode 100644
index 00000000000..68f0b90bfca
--- /dev/null
+++ b/tests/modules/programs/waybar/broken-settings.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ package = pkgs.writeScriptBin "dummy-waybar" "" // { outPath = "@waybar@"; };
+ expected = pkgs.writeText "expected-json" ''
+ [
+ {
+ "height": 26,
+ "layer": "top",
+ "modules-center": [
+ "sway/window"
+ ],
+ "modules-left": [
+ "sway/workspaces",
+ "sway/mode"
+ ],
+ "modules-right": [
+ "idle_inhibitor",
+ "pulseaudio",
+ "network",
+ "cpu",
+ "memory",
+ "backlight",
+ "tray",
+ "clock"
+ ],
+ "output": [
+ "DP-1",
+ "eDP-1",
+ "HEADLESS-1"
+ ],
+ "position": "top",
+ "sway/workspaces": {
+ "all-outputs": true
+ }
+ }
+ ]
+ '';
+in {
+ config = {
+ programs.waybar = {
+ inherit package;
+ enable = true;
+ systemd.enable = true;
+ settings = [{
+ layer = "top";
+ position = "top";
+ height = 26;
+ output = [ "DP-1" "eDP-1" "HEADLESS-1" ];
+ modules-left = [ "sway/workspaces" "sway/mode" ];
+ modules-center = [ "sway/window" ];
+ modules-right = [
+ "idle_inhibitor"
+ "pulseaudio"
+ "network"
+ "cpu"
+ "memory"
+ "backlight"
+ "tray"
+ "clock"
+ ];
+
+ modules = { "sway/workspaces".all-outputs = true; };
+ }];
+ };
+
+ nmt.description = ''
+ Test for the broken configuration
+ https://github.com/rycee/home-manager/pull/1329#issuecomment-653253069
+ '';
+ nmt.script = ''
+ assertPathNotExists home-files/.config/waybar/style.css
+ assertFileContent \
+ home-files/.config/waybar/config \
+ ${expected}
+ '';
+ };
+}
diff --git a/tests/modules/programs/waybar/default.nix b/tests/modules/programs/waybar/default.nix
new file mode 100644
index 00000000000..d50b1b8d368
--- /dev/null
+++ b/tests/modules/programs/waybar/default.nix
@@ -0,0 +1,8 @@
+{
+ waybar-systemd-with-graphical-session-target =
+ ./systemd-with-graphical-session-target.nix;
+ waybar-styling = ./styling.nix;
+ waybar-settings-complex = ./settings-complex.nix;
+ # Broken configuration from https://github.com/rycee/home-manager/pull/1329#issuecomment-653253069
+ waybar-broken-settings = ./broken-settings.nix;
+}
diff --git a/tests/modules/programs/waybar/settings-complex-expected.json b/tests/modules/programs/waybar/settings-complex-expected.json
new file mode 100644
index 00000000000..0d020c19c97
--- /dev/null
+++ b/tests/modules/programs/waybar/settings-complex-expected.json
@@ -0,0 +1,46 @@
+[
+ {
+ "custom/my-module": {
+ "exec": "@dummy@/bin/dummy",
+ "format": "hello from {}"
+ },
+ "height": 30,
+ "idle_inhibitor": {
+ "format": "{icon}"
+ },
+ "layer": "top",
+ "modules-center": [
+ "sway/window"
+ ],
+ "modules-left": [
+ "sway/workspaces",
+ "sway/mode",
+ "custom/my-module"
+ ],
+ "modules-right": [
+ "idle_inhibitor",
+ "pulseaudio",
+ "network",
+ "cpu",
+ "memory",
+ "backlight",
+ "tray",
+ "battery",
+ "clock"
+ ],
+ "output": [
+ "DP-1"
+ ],
+ "position": "top",
+ "sway/mode": {
+ "tooltip": false
+ },
+ "sway/window": {
+ "max-length": 120
+ },
+ "sway/workspaces": {
+ "all-outputs": true,
+ "disable-scroll": true
+ }
+ }
+]
diff --git a/tests/modules/programs/waybar/settings-complex.nix b/tests/modules/programs/waybar/settings-complex.nix
new file mode 100644
index 00000000000..750e52f4581
--- /dev/null
+++ b/tests/modules/programs/waybar/settings-complex.nix
@@ -0,0 +1,59 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ package = pkgs.writeScriptBin "dummy-waybar" "" // { outPath = "@waybar@"; };
+in {
+ config = {
+ programs.waybar = {
+ inherit package;
+ enable = true;
+ settings = [{
+ layer = "top";
+ position = "top";
+ height = 30;
+ output = [ "DP-1" ];
+ modules-left = [ "sway/workspaces" "sway/mode" "custom/my-module" ];
+ modules-center = [ "sway/window" ];
+ modules-right = [
+ "idle_inhibitor"
+ "pulseaudio"
+ "network"
+ "cpu"
+ "memory"
+ "backlight"
+ "tray"
+ "battery"
+ "clock"
+ ];
+
+ modules = {
+ "sway/workspaces" = {
+ disable-scroll = true;
+ all-outputs = true;
+ };
+ "sway/mode" = { tooltip = false; };
+ "sway/window" = { max-length = 120; };
+ "idle_inhibitor" = { format = "{icon}"; };
+ "custom/my-module" = {
+ format = "hello from {}";
+ exec = let
+ dummyScript =
+ pkgs.writeShellScriptBin "dummy" "echo within waybar" // {
+ outPath = "@dummy@";
+ };
+ in "${dummyScript}/bin/dummy";
+ };
+ };
+ }];
+ };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/waybar/style.css
+ assertFileContent \
+ home-files/.config/waybar/config \
+ ${./settings-complex-expected.json}
+ '';
+ };
+}
diff --git a/tests/modules/programs/waybar/styling-expected.css b/tests/modules/programs/waybar/styling-expected.css
new file mode 100644
index 00000000000..dc779e58770
--- /dev/null
+++ b/tests/modules/programs/waybar/styling-expected.css
@@ -0,0 +1,23 @@
+* {
+ border: none;
+ border-radius: 0;
+ font-family: Source Code Pro;
+ font-weight: bold;
+ color: #abb2bf;
+ font-size: 18px;
+ min-height: 0px;
+}
+window#waybar {
+ background: #16191C;
+ color: #aab2bf;
+}
+#window {
+ padding: 0 0px;
+}
+#workspaces button:hover {
+ box-shadow: inherit;
+ text-shadow: inherit;
+ background: #16191C;
+ border: #16191C;
+ padding: 0 3px;
+}
diff --git a/tests/modules/programs/waybar/styling.nix b/tests/modules/programs/waybar/styling.nix
new file mode 100644
index 00000000000..bd73f2aafd2
--- /dev/null
+++ b/tests/modules/programs/waybar/styling.nix
@@ -0,0 +1,46 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ package = pkgs.writeScriptBin "dummy-waybar" "" // { outPath = "@waybar@"; };
+in {
+ config = {
+ programs.waybar = {
+ inherit package;
+ enable = true;
+ style = ''
+ * {
+ border: none;
+ border-radius: 0;
+ font-family: Source Code Pro;
+ font-weight: bold;
+ color: #abb2bf;
+ font-size: 18px;
+ min-height: 0px;
+ }
+ window#waybar {
+ background: #16191C;
+ color: #aab2bf;
+ }
+ #window {
+ padding: 0 0px;
+ }
+ #workspaces button:hover {
+ box-shadow: inherit;
+ text-shadow: inherit;
+ background: #16191C;
+ border: #16191C;
+ padding: 0 3px;
+ }
+ '';
+ };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/waybar/config
+ assertFileContent \
+ home-files/.config/waybar/style.css \
+ ${./styling-expected.css}
+ '';
+ };
+}
diff --git a/tests/modules/programs/waybar/systemd-with-graphical-session-target.nix b/tests/modules/programs/waybar/systemd-with-graphical-session-target.nix
new file mode 100644
index 00000000000..e751d804dab
--- /dev/null
+++ b/tests/modules/programs/waybar/systemd-with-graphical-session-target.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ package = pkgs.writeScriptBin "dummy-waybar" "" // { outPath = "@waybar@"; };
+in {
+ config = {
+ programs.waybar = {
+ inherit package;
+ enable = true;
+ systemd.enable = true;
+ };
+
+ nmt.script = ''
+ assertPathNotExists home-files/.config/waybar/config
+ assertPathNotExists home-files/.config/waybar/style.css
+
+ assertFileContent \
+ home-files/.config/systemd/user/waybar.service \
+ ${./systemd-with-graphical-session-target.service}
+ '';
+ };
+}
diff --git a/tests/modules/programs/waybar/systemd-with-graphical-session-target.service b/tests/modules/programs/waybar/systemd-with-graphical-session-target.service
new file mode 100644
index 00000000000..7d4c65214e8
--- /dev/null
+++ b/tests/modules/programs/waybar/systemd-with-graphical-session-target.service
@@ -0,0 +1,16 @@
+[Install]
+WantedBy=graphical-session.target
+
+[Service]
+BusName=fr.arouillard.waybar
+ExecStart=@waybar@/bin/waybar
+Restart=always
+RestartSec=1sec
+Type=dbus
+
+[Unit]
+After=dbus.service
+Description=Highly customizable Wayland bar for Sway and Wlroots based compositors.
+Documentation=https://github.com/Alexays/Waybar/wiki
+PartOf=graphical-session.target
+Requisite=dbus.service