From e333d443d9aa546dd84a716e3d37a6ca2330bfde Mon Sep 17 00:00:00 2001 From: Liam Jarvis Date: Wed, 27 Dec 2023 14:32:50 +0900 Subject: [PATCH] feat: Rework configuration setup to add config validation This commit reworks the config setup structure and adds Config typing, validations and tests. On config setup the user's configuration will be validated against. Any type errors raised by invalid settings will result in a user-friendly error message being shown to the user. Signed-off-by: Liam Jarvis --- lua/octo/commands.lua | 4 +- lua/octo/config.lua | 611 ++++++++++++------ lua/octo/gh/init.lua | 4 +- lua/octo/model/octo-buffer.lua | 4 +- lua/octo/picker.lua | 2 +- .../pickers/fzf-lua/pickers/fzf_actions.lua | 2 +- lua/octo/pickers/fzf-lua/pickers/issues.lua | 2 +- lua/octo/pickers/fzf-lua/pickers/prs.lua | 2 +- lua/octo/pickers/fzf-lua/previewers.lua | 2 +- lua/octo/pickers/telescope/provider.lua | 8 +- lua/octo/reviews/file-entry.lua | 8 +- lua/octo/reviews/file-panel.lua | 6 +- lua/octo/reviews/init.lua | 4 +- lua/octo/reviews/renderer.lua | 2 +- lua/octo/ui/bubbles.lua | 6 +- lua/octo/ui/colors.lua | 2 +- lua/octo/ui/signs.lua | 2 +- lua/octo/ui/writers.lua | 26 +- lua/octo/utils.lua | 6 +- lua/tests/octo/config_spec.lua | 146 +++++ lua/tests/plenary/config_spec.lua | 4 +- 21 files changed, 612 insertions(+), 241 deletions(-) create mode 100644 lua/tests/octo/config_spec.lua diff --git a/lua/octo/commands.lua b/lua/octo/commands.lua index c20c7f19..72afa1ca 100644 --- a/lua/octo/commands.lua +++ b/lua/octo/commands.lua @@ -341,7 +341,7 @@ function M.octo(object, action, ...) end if not object then - if config.get_config().enable_builtin then + if config.values.enable_builtin then M.commands.actions() else utils.error "Missing arguments" @@ -759,7 +759,7 @@ end function M.create_pr(is_draft) is_draft = "draft" == is_draft and true or false - local conf = config.get_config() + local conf = config.values local select = conf.pull_requests.always_select_remote_on_create or false local repo diff --git a/lua/octo/config.lua b/lua/octo/config.lua index 262eecd4..deb94c76 100644 --- a/lua/octo/config.lua +++ b/lua/octo/config.lua @@ -1,211 +1,436 @@ local M = {} -M.defaults = { - picker = "telescope", - picker_config = { - use_emojis = false, - mappings = { - open_in_browser = { lhs = "", desc = "open issue in browser" }, - copy_url = { lhs = "", desc = "copy url to system clipboard" }, - checkout_pr = { lhs = "", desc = "checkout pull request" }, - merge_pr = { lhs = "", desc = "merge pull request" }, - }, - }, - default_remote = { "upstream", "origin" }, - ssh_aliases = {}, - reaction_viewer_hint_icon = " ", - user_icon = " ", - comment_icon = "▎", - outdated_icon = "󰅒 ", - resolved_icon = " ", - timeline_marker = " ", - timeline_indent = "2", - right_bubble_delimiter = "", - left_bubble_delimiter = "", - github_hostname = "", - use_local_fs = false, - enable_builtin = false, - snippet_context_lines = 4, - gh_env = {}, - timeout = 5000, - ui = { - use_signcolumn = true, - }, - issues = { - order_by = { - field = "CREATED_AT", - direction = "DESC", - }, - }, - pull_requests = { - order_by = { - field = "CREATED_AT", - direction = "DESC", +---@alias OctoMappingsWindow "issue" | "pull_request" | "review_thread" | "submit_win" | "review_diff" | "file_panel" | "repo" +---@alias OctoMappingsList { [string]: table} +---@alias OctoPickers "telescope" | "fzf-lua" + +---@class OctoPickerConfig +---@field use_emojis boolean +---@field mappings table + +---@class OctoConfigColors +---@field white string +---@field grey string +---@field black string +---@field red string +---@field dark_red string +---@field green string +---@field dark_green string +---@field yellow string +---@field dark_yellow string +---@field blue string +---@field dark_blue string +---@field purple string + +---@class OctoConfigFilePanel +---@field size number +---@field use_icons boolean + +---@class OctoConfigUi +---@field use_signcolumn boolean + +---@class OctoConfigIssues +---@field order_by OctoConfigOrderBy + +---@class OctoConfigPR +---@field order_by OctoConfigOrderBy +---@field always_select_remote_on_create boolean + +---@class OctoConfigOrderBy +---@field field string +---@field direction "ASC" | "DESC" + +---@class OctoConfig Octo configuration settings +---@field picker OctoPickers +---@field picker_config OctoPickerConfig +---@field default_remote table +---@field ssh_aliases {[string]:string} +---@field reaction_viewer_hint_icon string +---@field user_icon string +---@field comment_icon string +---@field outdated_icon string +---@field resolved_icon string +---@field timeline_marker string +---@field timeline_indent string +---@field right_bubble_delimiter string +---@field left_bubble_delimiter string +---@field github_hostname string +---@field use_local_fs boolean +---@field enable_builtin boolean +---@field snippet_context_lines number +---@field gh_env table +---@field timeout number +---@field ui OctoConfigUi +---@field issues OctoConfigIssues +---@field pull_requests OctoConfigPR +---@field file_panel OctoConfigFilePanel +---@field colors OctoConfigColors +---@field mappings { [OctoMappingsWindow]: OctoMappingsList} + +--- Returns the default octo config values +---@return OctoConfig +function M.get_default_values() + return { + picker = "telescope", + picker_config = { + use_emojis = false, + mappings = { + open_in_browser = { lhs = "", desc = "open issue in browser" }, + copy_url = { lhs = "", desc = "copy url to system clipboard" }, + checkout_pr = { lhs = "", desc = "checkout pull request" }, + merge_pr = { lhs = "", desc = "merge pull request" }, + }, }, - always_select_remote_on_create = false, - }, - file_panel = { - size = 10, - use_icons = true, - }, - colors = { - white = "#ffffff", - grey = "#2A354C", - black = "#000000", - red = "#fdb8c0", - dark_red = "#da3633", - green = "#acf2bd", - dark_green = "#238636", - yellow = "#d3c846", - dark_yellow = "#735c0f", - blue = "#58A6FF", - dark_blue = "#0366d6", - purple = "#6f42c1", - }, - mappings = { - issue = { - close_issue = { lhs = "ic", desc = "close issue" }, - reopen_issue = { lhs = "io", desc = "reopen issue" }, - list_issues = { lhs = "il", desc = "list open issues on same repo" }, - reload = { lhs = "", desc = "reload issue" }, - open_in_browser = { lhs = "", desc = "open issue in browser" }, - copy_url = { lhs = "", desc = "copy url to system clipboard" }, - add_assignee = { lhs = "aa", desc = "add assignee" }, - remove_assignee = { lhs = "ad", desc = "remove assignee" }, - create_label = { lhs = "lc", desc = "create label" }, - add_label = { lhs = "la", desc = "add label" }, - remove_label = { lhs = "ld", desc = "remove label" }, - goto_issue = { lhs = "gi", desc = "navigate to a local repo issue" }, - add_comment = { lhs = "ca", desc = "add comment" }, - delete_comment = { lhs = "cd", desc = "delete comment" }, - next_comment = { lhs = "]c", desc = "go to next comment" }, - prev_comment = { lhs = "[c", desc = "go to previous comment" }, - react_hooray = { lhs = "rp", desc = "add/remove 🎉 reaction" }, - react_heart = { lhs = "rh", desc = "add/remove ❤️ reaction" }, - react_eyes = { lhs = "re", desc = "add/remove 👀 reaction" }, - react_thumbs_up = { lhs = "r+", desc = "add/remove 👍 reaction" }, - react_thumbs_down = { lhs = "r-", desc = "add/remove 👎 reaction" }, - react_rocket = { lhs = "rr", desc = "add/remove 🚀 reaction" }, - react_laugh = { lhs = "rl", desc = "add/remove 😄 reaction" }, - react_confused = { lhs = "rc", desc = "add/remove 😕 reaction" }, + default_remote = { "upstream", "origin" }, + ssh_aliases = {}, + reaction_viewer_hint_icon = " ", + user_icon = " ", + comment_icon = "▎", + outdated_icon = "󰅒 ", + resolved_icon = " ", + timeline_marker = " ", + timeline_indent = "2", + right_bubble_delimiter = "", + left_bubble_delimiter = "", + github_hostname = "", + use_local_fs = false, + enable_builtin = false, + snippet_context_lines = 4, + gh_env = {}, + timeout = 5000, + ui = { + use_signcolumn = true, }, - pull_request = { - checkout_pr = { lhs = "po", desc = "checkout PR" }, - merge_pr = { lhs = "pm", desc = "merge commit PR" }, - squash_and_merge_pr = { lhs = "psm", desc = "squash and merge PR" }, - list_commits = { lhs = "pc", desc = "list PR commits" }, - list_changed_files = { lhs = "pf", desc = "list PR changed files" }, - show_pr_diff = { lhs = "pd", desc = "show PR diff" }, - add_reviewer = { lhs = "va", desc = "add reviewer" }, - remove_reviewer = { lhs = "vd", desc = "remove reviewer request" }, - close_issue = { lhs = "ic", desc = "close PR" }, - reopen_issue = { lhs = "io", desc = "reopen PR" }, - list_issues = { lhs = "il", desc = "list open issues on same repo" }, - reload = { lhs = "", desc = "reload PR" }, - open_in_browser = { lhs = "", desc = "open PR in browser" }, - copy_url = { lhs = "", desc = "copy url to system clipboard" }, - goto_file = { lhs = "gf", desc = "go to file" }, - add_assignee = { lhs = "aa", desc = "add assignee" }, - remove_assignee = { lhs = "ad", desc = "remove assignee" }, - create_label = { lhs = "lc", desc = "create label" }, - add_label = { lhs = "la", desc = "add label" }, - remove_label = { lhs = "ld", desc = "remove label" }, - goto_issue = { lhs = "gi", desc = "navigate to a local repo issue" }, - add_comment = { lhs = "ca", desc = "add comment" }, - delete_comment = { lhs = "cd", desc = "delete comment" }, - next_comment = { lhs = "]c", desc = "go to next comment" }, - prev_comment = { lhs = "[c", desc = "go to previous comment" }, - react_hooray = { lhs = "rp", desc = "add/remove 🎉 reaction" }, - react_heart = { lhs = "rh", desc = "add/remove ❤️ reaction" }, - react_eyes = { lhs = "re", desc = "add/remove 👀 reaction" }, - react_thumbs_up = { lhs = "r+", desc = "add/remove 👍 reaction" }, - react_thumbs_down = { lhs = "r-", desc = "add/remove 👎 reaction" }, - react_rocket = { lhs = "rr", desc = "add/remove 🚀 reaction" }, - react_laugh = { lhs = "rl", desc = "add/remove 😄 reaction" }, - react_confused = { lhs = "rc", desc = "add/remove 😕 reaction" }, + issues = { + order_by = { + field = "CREATED_AT", + direction = "DESC", + }, }, - review_thread = { - goto_issue = { lhs = "gi", desc = "navigate to a local repo issue" }, - add_comment = { lhs = "ca", desc = "add comment" }, - add_suggestion = { lhs = "sa", desc = "add suggestion" }, - delete_comment = { lhs = "cd", desc = "delete comment" }, - next_comment = { lhs = "]c", desc = "go to next comment" }, - prev_comment = { lhs = "[c", desc = "go to previous comment" }, - select_next_entry = { lhs = "]q", desc = "move to previous changed file" }, - select_prev_entry = { lhs = "[q", desc = "move to next changed file" }, - select_first_entry = { lhs = "[Q", desc = "move to first changed file" }, - select_last_entry = { lhs = "]Q", desc = "move to last changed file" }, - close_review_tab = { lhs = "", desc = "close review tab" }, - react_hooray = { lhs = "rp", desc = "add/remove 🎉 reaction" }, - react_heart = { lhs = "rh", desc = "add/remove ❤️ reaction" }, - react_eyes = { lhs = "re", desc = "add/remove 👀 reaction" }, - react_thumbs_up = { lhs = "r+", desc = "add/remove 👍 reaction" }, - react_thumbs_down = { lhs = "r-", desc = "add/remove 👎 reaction" }, - react_rocket = { lhs = "rr", desc = "add/remove 🚀 reaction" }, - react_laugh = { lhs = "rl", desc = "add/remove 😄 reaction" }, - react_confused = { lhs = "rc", desc = "add/remove 😕 reaction" }, + pull_requests = { + order_by = { + field = "CREATED_AT", + direction = "DESC", + }, + always_select_remote_on_create = false, }, - submit_win = { - approve_review = { lhs = "", desc = "approve review" }, - comment_review = { lhs = "", desc = "comment review" }, - request_changes = { lhs = "", desc = "request changes review" }, - close_review_tab = { lhs = "", desc = "close review tab" }, + file_panel = { + size = 10, + use_icons = true, }, - review_diff = { - add_review_comment = { lhs = "ca", desc = "add a new review comment" }, - add_review_suggestion = { lhs = "sa", desc = "add a new review suggestion" }, - focus_files = { lhs = "e", desc = "move focus to changed file panel" }, - toggle_files = { lhs = "b", desc = "hide/show changed files panel" }, - next_thread = { lhs = "]t", desc = "move to next thread" }, - prev_thread = { lhs = "[t", desc = "move to previous thread" }, - select_next_entry = { lhs = "]q", desc = "move to previous changed file" }, - select_prev_entry = { lhs = "[q", desc = "move to next changed file" }, - select_first_entry = { lhs = "[Q", desc = "move to first changed file" }, - select_last_entry = { lhs = "]Q", desc = "move to last changed file" }, - close_review_tab = { lhs = "", desc = "close review tab" }, - toggle_viewed = { lhs = "", desc = "toggle viewer viewed state" }, - goto_file = { lhs = "gf", desc = "go to file" }, + colors = { + white = "#ffffff", + grey = "#2A354C", + black = "#000000", + red = "#fdb8c0", + dark_red = "#da3633", + green = "#acf2bd", + dark_green = "#238636", + yellow = "#d3c846", + dark_yellow = "#735c0f", + blue = "#58A6FF", + dark_blue = "#0366d6", + purple = "#6f42c1", }, - file_panel = { - next_entry = { lhs = "j", desc = "move to next changed file" }, - prev_entry = { lhs = "k", desc = "move to previous changed file" }, - select_entry = { lhs = "", desc = "show selected changed file diffs" }, - refresh_files = { lhs = "R", desc = "refresh changed files panel" }, - focus_files = { lhs = "e", desc = "move focus to changed file panel" }, - toggle_files = { lhs = "b", desc = "hide/show changed files panel" }, - select_next_entry = { lhs = "]q", desc = "move to previous changed file" }, - select_prev_entry = { lhs = "[q", desc = "move to next changed file" }, - select_first_entry = { lhs = "[Q", desc = "move to first changed file" }, - select_last_entry = { lhs = "]Q", desc = "move to last changed file" }, - close_review_tab = { lhs = "", desc = "close review tab" }, - toggle_viewed = { lhs = "", desc = "toggle viewer viewed state" }, + mappings = { + issue = { + close_issue = { lhs = "ic", desc = "close issue" }, + reopen_issue = { lhs = "io", desc = "reopen issue" }, + list_issues = { lhs = "il", desc = "list open issues on same repo" }, + reload = { lhs = "", desc = "reload issue" }, + open_in_browser = { lhs = "", desc = "open issue in browser" }, + copy_url = { lhs = "", desc = "copy url to system clipboard" }, + add_assignee = { lhs = "aa", desc = "add assignee" }, + remove_assignee = { lhs = "ad", desc = "remove assignee" }, + create_label = { lhs = "lc", desc = "create label" }, + add_label = { lhs = "la", desc = "add label" }, + remove_label = { lhs = "ld", desc = "remove label" }, + goto_issue = { lhs = "gi", desc = "navigate to a local repo issue" }, + add_comment = { lhs = "ca", desc = "add comment" }, + delete_comment = { lhs = "cd", desc = "delete comment" }, + next_comment = { lhs = "]c", desc = "go to next comment" }, + prev_comment = { lhs = "[c", desc = "go to previous comment" }, + react_hooray = { lhs = "rp", desc = "add/remove 🎉 reaction" }, + react_heart = { lhs = "rh", desc = "add/remove ❤️ reaction" }, + react_eyes = { lhs = "re", desc = "add/remove 👀 reaction" }, + react_thumbs_up = { lhs = "r+", desc = "add/remove 👍 reaction" }, + react_thumbs_down = { lhs = "r-", desc = "add/remove 👎 reaction" }, + react_rocket = { lhs = "rr", desc = "add/remove 🚀 reaction" }, + react_laugh = { lhs = "rl", desc = "add/remove 😄 reaction" }, + react_confused = { lhs = "rc", desc = "add/remove 😕 reaction" }, + }, + pull_request = { + checkout_pr = { lhs = "po", desc = "checkout PR" }, + merge_pr = { lhs = "pm", desc = "merge commit PR" }, + squash_and_merge_pr = { lhs = "psm", desc = "squash and merge PR" }, + list_commits = { lhs = "pc", desc = "list PR commits" }, + list_changed_files = { lhs = "pf", desc = "list PR changed files" }, + show_pr_diff = { lhs = "pd", desc = "show PR diff" }, + add_reviewer = { lhs = "va", desc = "add reviewer" }, + remove_reviewer = { lhs = "vd", desc = "remove reviewer request" }, + close_issue = { lhs = "ic", desc = "close PR" }, + reopen_issue = { lhs = "io", desc = "reopen PR" }, + list_issues = { lhs = "il", desc = "list open issues on same repo" }, + reload = { lhs = "", desc = "reload PR" }, + open_in_browser = { lhs = "", desc = "open PR in browser" }, + copy_url = { lhs = "", desc = "copy url to system clipboard" }, + goto_file = { lhs = "gf", desc = "go to file" }, + add_assignee = { lhs = "aa", desc = "add assignee" }, + remove_assignee = { lhs = "ad", desc = "remove assignee" }, + create_label = { lhs = "lc", desc = "create label" }, + add_label = { lhs = "la", desc = "add label" }, + remove_label = { lhs = "ld", desc = "remove label" }, + goto_issue = { lhs = "gi", desc = "navigate to a local repo issue" }, + add_comment = { lhs = "ca", desc = "add comment" }, + delete_comment = { lhs = "cd", desc = "delete comment" }, + next_comment = { lhs = "]c", desc = "go to next comment" }, + prev_comment = { lhs = "[c", desc = "go to previous comment" }, + react_hooray = { lhs = "rp", desc = "add/remove 🎉 reaction" }, + react_heart = { lhs = "rh", desc = "add/remove ❤️ reaction" }, + react_eyes = { lhs = "re", desc = "add/remove 👀 reaction" }, + react_thumbs_up = { lhs = "r+", desc = "add/remove 👍 reaction" }, + react_thumbs_down = { lhs = "r-", desc = "add/remove 👎 reaction" }, + react_rocket = { lhs = "rr", desc = "add/remove 🚀 reaction" }, + react_laugh = { lhs = "rl", desc = "add/remove 😄 reaction" }, + react_confused = { lhs = "rc", desc = "add/remove 😕 reaction" }, + }, + review_thread = { + goto_issue = { lhs = "gi", desc = "navigate to a local repo issue" }, + add_comment = { lhs = "ca", desc = "add comment" }, + add_suggestion = { lhs = "sa", desc = "add suggestion" }, + delete_comment = { lhs = "cd", desc = "delete comment" }, + next_comment = { lhs = "]c", desc = "go to next comment" }, + prev_comment = { lhs = "[c", desc = "go to previous comment" }, + select_next_entry = { lhs = "]q", desc = "move to previous changed file" }, + select_prev_entry = { lhs = "[q", desc = "move to next changed file" }, + select_first_entry = { lhs = "[Q", desc = "move to first changed file" }, + select_last_entry = { lhs = "]Q", desc = "move to last changed file" }, + close_review_tab = { lhs = "", desc = "close review tab" }, + react_hooray = { lhs = "rp", desc = "add/remove 🎉 reaction" }, + react_heart = { lhs = "rh", desc = "add/remove ❤️ reaction" }, + react_eyes = { lhs = "re", desc = "add/remove 👀 reaction" }, + react_thumbs_up = { lhs = "r+", desc = "add/remove 👍 reaction" }, + react_thumbs_down = { lhs = "r-", desc = "add/remove 👎 reaction" }, + react_rocket = { lhs = "rr", desc = "add/remove 🚀 reaction" }, + react_laugh = { lhs = "rl", desc = "add/remove 😄 reaction" }, + react_confused = { lhs = "rc", desc = "add/remove 😕 reaction" }, + }, + submit_win = { + approve_review = { lhs = "", desc = "approve review" }, + comment_review = { lhs = "", desc = "comment review" }, + request_changes = { lhs = "", desc = "request changes review" }, + close_review_tab = { lhs = "", desc = "close review tab" }, + }, + review_diff = { + add_review_comment = { lhs = "ca", desc = "add a new review comment" }, + add_review_suggestion = { lhs = "sa", desc = "add a new review suggestion" }, + focus_files = { lhs = "e", desc = "move focus to changed file panel" }, + toggle_files = { lhs = "b", desc = "hide/show changed files panel" }, + next_thread = { lhs = "]t", desc = "move to next thread" }, + prev_thread = { lhs = "[t", desc = "move to previous thread" }, + select_next_entry = { lhs = "]q", desc = "move to previous changed file" }, + select_prev_entry = { lhs = "[q", desc = "move to next changed file" }, + select_first_entry = { lhs = "[Q", desc = "move to first changed file" }, + select_last_entry = { lhs = "]Q", desc = "move to last changed file" }, + close_review_tab = { lhs = "", desc = "close review tab" }, + toggle_viewed = { lhs = "", desc = "toggle viewer viewed state" }, + goto_file = { lhs = "gf", desc = "go to file" }, + }, + file_panel = { + next_entry = { lhs = "j", desc = "move to next changed file" }, + prev_entry = { lhs = "k", desc = "move to previous changed file" }, + select_entry = { lhs = "", desc = "show selected changed file diffs" }, + refresh_files = { lhs = "R", desc = "refresh changed files panel" }, + focus_files = { lhs = "e", desc = "move focus to changed file panel" }, + toggle_files = { lhs = "b", desc = "hide/show changed files panel" }, + select_next_entry = { lhs = "]q", desc = "move to previous changed file" }, + select_prev_entry = { lhs = "[q", desc = "move to next changed file" }, + select_first_entry = { lhs = "[Q", desc = "move to first changed file" }, + select_last_entry = { lhs = "]Q", desc = "move to last changed file" }, + close_review_tab = { lhs = "", desc = "close review tab" }, + toggle_viewed = { lhs = "", desc = "toggle viewer viewed state" }, + }, + repo = {}, }, - repo = {}, - }, -} + } +end + +M.values = M.get_default_values() -local config = {} +---Validates the config +---@return { [string]: string } all error messages emitted during validation +function M.validate_config() + local config = M.values + + ---@type { [string]: string } + local errors = {} + local function err(value, msg) + errors[value] = msg + end -function M.get_config() - return config or M.defaults + ---Checks if a variable is the correct, type if not it calls err with an error string + ---@param value any + ---@param name string + ---@param expected_types type | type[] + local function validate_type(value, name, expected_types) + if type(expected_types) == "table" then + if not vim.tbl_contains(expected_types, type(value)) then + err( + name, + string.format( + "Expected `%s` to be one of types '%s', got '%s'", + name, + table.concat(expected_types, ", "), + type(value) + ) + ) + return false + end + return true + end + + if type(value) ~= expected_types then + err(name, string.format("Expected `%s` to be of type '%s', got '%s'", name, expected_types, type(value))) + return false + end + return true + end + + local function validate_pickers() + local valid_pickers = { "telescope", "fzf-lua" } + if not validate_type(config.picker, "picker", "string") then + return + end + if not vim.tbl_contains(valid_pickers, config.picker) then + err( + "picker." .. config.picker, + string.format( + "Expected a valid picker, received '%s', which is not a supported picker! Valid pickers: ", + config.picker, + table.concat(valid_pickers, ", ") + ) + ) + end + if not validate_type(config.picker_config, "picker_config", "table") then + return + end + + validate_type(config.picker_config.use_emojis, "picker_config.use_emojis", "boolean") + validate_type(config.picker_config.mappings, "picker_config.mappings", "table") + end + + local function validate_aliases() + if not validate_type(config.ssh_aliases, "ssh_aliases", "table") then + return + end + for name, alias in pairs(config.ssh_aliases) do + validate_type(alias, string.format("ssh_aliases.%s", name), "string") + end + end + + local function validate_issues() + if not validate_type(config.issues, "issues", "table") then + return + end + if validate_type(config.issues.order_by, "issues.order_by", "table") then + validate_type(config.issues.order_by.field, "issues.order_by.field", "string") + validate_type(config.issues.order_by.direction, "issues.order_by.direction", "string") + end + end + + local function validate_pull_requests() + if not validate_type(config.pull_requests, "pull_requests", "table") then + return + end + if validate_type(config.pull_requests.order_by, "pull_requests.order_by", "table") then + validate_type(config.pull_requests.order_by.field, "pull_requests.order_by.field", "string") + validate_type(config.pull_requests.order_by.direction, "pull_requests.order_by.direction", "string") + end + validate_type(config.pull_requests.always_select_remote_on_create, "always_select_remote_on_create", "boolean") + end + + local function validate_mappings() + -- TODO(jarviliam): Validate each keymap + if not validate_type(config.mappings, "mappings", "table") then + return + end + end + + if validate_type(config, "base config", "table") then + validate_type(config.use_local_fs, "use_local_fs", "boolean") + validate_type(config.enable_builtin, "enable_builtin", "boolean") + validate_type(config.snippet_context_lines, "snippet_context_lines", "number") + validate_type(config.timeout, "timeout", "number") + validate_type(config.gh_env, "gh_env", "table") + validate_type(config.reaction_viewer_hint_icon, "reaction_viewer_hint_icon", "string") + validate_type(config.user_icon, "user_icon", "string") + validate_type(config.comment_icon, "comment_icon", "string") + validate_type(config.outdated_icon, "outdated_icon", "string") + validate_type(config.resolved_icon, "resolved_icon", "string") + validate_type(config.timeline_marker, "timeline_marker", "string") + validate_type(config.timeline_indent, "timeline_indent", "string") + validate_type(config.right_bubble_delimiter, "right_bubble_delimiter", "string") + validate_type(config.left_bubble_delimiter, "left_bubble_delimiter", "string") + validate_type(config.github_hostname, "github_hostname", "string") + if validate_type(config.default_remote, "default_remote", "table") then + for _, v in ipairs(config.default_remote) do + validate_type(v, "remote", "string") + end + end + if validate_type(config.ui, "ui", "table") then + validate_type(config.ui.use_signcolumn, "ui.use_signcolumn", "boolean") + end + if validate_type(config.colors, "colors", "table") then + for k, v in pairs(config.colors) do + validate_type(v, string.format("colors.%s", k), "string") + end + end + + validate_issues() + validate_pull_requests() + if validate_type(config.file_panel, "file_panel", "table") then + validate_type(config.file_panel.size, "file_panel.size", "number") + validate_type(config.file_panel.use_icons, "file_panel.use_icons", "boolean") + end + validate_aliases() + validate_pickers() + validate_mappings() + end + + return errors end -function M.setup(user_config) - user_config = user_config or {} - config = vim.tbl_deep_extend("force", M.defaults, user_config) - - -- If the user provides key bindings: use only the user bindings. - if user_config.mappings then - config.mappings.issue = (user_config.mappings.issue or M.defaults.mappings.issue) - config.mappings.pull_request = (user_config.mappings.pull_request or M.defaults.mappings.pull_request) - config.mappings.review_thread = (user_config.mappings.review_thread or M.defaults.mappings.review_thread) - config.mappings.review = (user_config.mappings.review or M.defaults.mappings.review) - config.mappings.file_panel = (user_config.mappings.file_panel or M.defaults.mappings.file_panel) - config.mappings.submit_win = (user_config.mappings.submit_win or M.defaults.mappings.submit_win) - config.mappings.repo = (user_config.mappings.repo or M.defaults.mappings.repo) +function M.setup(opts) + if opts ~= nil then + M.values = vim.tbl_deep_extend("force", M.values, opts) end + local config_errs = M.validate_config() + if vim.tbl_count(config_errs) > 0 then + local header = "====Octo Configuration Errors====" + local header_message = { + "You have a misconfiguration in your octo setup!", + 'Validate that your configuration passed to `require("octo").setup()` is valid!', + } + local header_sep = "" + for _ = 0, string.len(header), 1 do + header_sep = header_sep .. "-" + end - return config + local config_errs_message = {} + for config_key, err in pairs(config_errs) do + table.insert(config_errs_message, string.format("Config value: `%s` had error -> %s", config_key, err)) + end + error( + string.format( + "\n%s\n%s\n%s\n%s", + header, + table.concat(header_message, "\n"), + header_sep, + table.concat(config_errs_message, "\n") + ), + vim.log.levels.ERROR + ) + end end return M diff --git a/lua/octo/gh/init.lua b/lua/octo/gh/init.lua index 636877ed..e682dcc8 100644 --- a/lua/octo/gh/init.lua +++ b/lua/octo/gh/init.lua @@ -28,7 +28,7 @@ local env_vars = { local function get_env() local env = env_vars - local gh_env = config.get_config().gh_env + local gh_env = config.values.gh_env if type(gh_env) == "function" then local computed_env = gh_env() if type(computed_env) == "table" then @@ -79,7 +79,7 @@ function M.run(opts) end opts = opts or {} - local conf = config.get_config() + local conf = config.values local mode = opts.mode or "async" local hostname = "" if opts.args[1] == "api" then diff --git a/lua/octo/model/octo-buffer.lua b/lua/octo/model/octo-buffer.lua index 9a76a2c9..3da856c7 100644 --- a/lua/octo/model/octo-buffer.lua +++ b/lua/octo/model/octo-buffer.lua @@ -237,7 +237,7 @@ end function OctoBuffer:configure() -- configure buffer vim.api.nvim_buf_call(self.bufnr, function() - local use_signcolumn = config.get_config().ui.use_signcolumn + local use_signcolumn = config.values.ui.use_signcolumn vim.cmd [[setlocal filetype=octo]] vim.cmd [[setlocal buftype=acwrite]] vim.cmd [[setlocal omnifunc=v:lua.octo_omnifunc]] @@ -799,7 +799,7 @@ end ---Renders the signcolumn function OctoBuffer:render_signcolumn() - local use_signcolumn = config.get_config().ui.use_signcolumn + local use_signcolumn = config.values.ui.use_signcolumn if not use_signcolumn or not self.ready then return end diff --git a/lua/octo/picker.lua b/lua/octo/picker.lua index 73abe6c6..91cefd42 100644 --- a/lua/octo/picker.lua +++ b/lua/octo/picker.lua @@ -4,7 +4,7 @@ local utils = require "octo.utils" local M = {} function M.setup() - local provider_name = config.get_config().picker + local provider_name = config.values.picker if utils.is_blank(provider_name) then provider_name = "telescope" end diff --git a/lua/octo/pickers/fzf-lua/pickers/fzf_actions.lua b/lua/octo/pickers/fzf-lua/pickers/fzf_actions.lua index d05ef845..05c8d2d8 100644 --- a/lua/octo/pickers/fzf-lua/pickers/fzf_actions.lua +++ b/lua/octo/pickers/fzf-lua/pickers/fzf_actions.lua @@ -21,7 +21,7 @@ M.common_buffer_actions = function(formatted_items) end M.common_open_actions = function(formatted_items) - local cfg = octo_config.get_config() + local cfg = octo_config.values return vim.tbl_extend("force", M.common_buffer_actions(formatted_items), { [utils.convert_vim_mapping_to_fzf(cfg.picker_config.mappings.open_in_browser.lhs)] = function(selected) picker_utils.open_in_browser(formatted_items[selected[1]]) diff --git a/lua/octo/pickers/fzf-lua/pickers/issues.lua b/lua/octo/pickers/fzf-lua/pickers/issues.lua index 5410f5cc..e95f2143 100644 --- a/lua/octo/pickers/fzf-lua/pickers/issues.lua +++ b/lua/octo/pickers/fzf-lua/pickers/issues.lua @@ -23,7 +23,7 @@ return function(opts) end local owner, name = utils.split_repo(opts.repo) - local cfg = octo_config.get_config() + local cfg = octo_config.values local order_by = cfg.issues.order_by local query = graphql("issues_query", owner, name, filter, order_by.field, order_by.direction, { escape = false }) diff --git a/lua/octo/pickers/fzf-lua/pickers/prs.lua b/lua/octo/pickers/fzf-lua/pickers/prs.lua index ad925340..380455b7 100644 --- a/lua/octo/pickers/fzf-lua/pickers/prs.lua +++ b/lua/octo/pickers/fzf-lua/pickers/prs.lua @@ -27,7 +27,7 @@ return function(opts) end local owner, name = utils.split_repo(opts.repo) - local cfg = octo_config.get_config() + local cfg = octo_config.values local order_by = cfg.pull_requests.order_by local query = diff --git a/lua/octo/pickers/fzf-lua/previewers.lua b/lua/octo/pickers/fzf-lua/previewers.lua index 91166cdc..062d7a42 100644 --- a/lua/octo/pickers/fzf-lua/previewers.lua +++ b/lua/octo/pickers/fzf-lua/previewers.lua @@ -324,7 +324,7 @@ M.repo = function(formatted_repos) self:set_preview_buf(tmpbuf) local stargazer, fork - if config.get_config().picker_config.use_emojis then + if config.values.picker_config.use_emojis then stargazer = string.format("💫: %s", entry.repo.stargazerCount) fork = string.format("🔱: %s", entry.repo.forkCount) else diff --git a/lua/octo/pickers/telescope/provider.lua b/lua/octo/pickers/telescope/provider.lua index 4f3830ca..a4afc37f 100644 --- a/lua/octo/pickers/telescope/provider.lua +++ b/lua/octo/pickers/telescope/provider.lua @@ -132,7 +132,7 @@ function M.issues(opts) end local owner, name = utils.split_repo(opts.repo) - local cfg = octo_config.get_config() + local cfg = octo_config.values local order_by = cfg.issues.order_by local query = graphql("issues_query", owner, name, filter, order_by.field, order_by.direction, { escape = false }) utils.info "Fetching issues (this may take a while) ..." @@ -277,7 +277,7 @@ function M.pull_requests(opts) end local owner, name = utils.split_repo(opts.repo) - local cfg = octo_config.get_config() + local cfg = octo_config.values local order_by = cfg.pull_requests.order_by local query = graphql("pull_requests_query", owner, name, filter, order_by.field, order_by.direction, { escape = false }) @@ -479,7 +479,7 @@ end --- function M.search(opts) opts = opts or {} - local cfg = octo_config.get_config() + local cfg = octo_config.values local requester = function() return function(prompt) if not opts.prompt and utils.is_blank(prompt) then @@ -924,7 +924,7 @@ end -- function M.repos(opts) opts = opts or {} - local cfg = octo_config.get_config() + local cfg = octo_config.values if not opts.login then if vim.g.octo_viewer then opts.login = vim.g.octo_viewer diff --git a/lua/octo/reviews/file-entry.lua b/lua/octo/reviews/file-entry.lua index d35562e6..75a12559 100644 --- a/lua/octo/reviews/file-entry.lua +++ b/lua/octo/reviews/file-entry.lua @@ -176,7 +176,7 @@ function FileEntry:fetch() local left_sha = current_review.layout.left.commit local right_abbrev = current_review.layout.right:abbrev() local left_abbrev = current_review.layout.left:abbrev() - local conf = config.get_config() + local conf = config.values -- handle renamed files if self.status == "R" and self.previous_path then @@ -236,7 +236,7 @@ function FileEntry:load_buffers(left_winid, right_winid) -- configure diff buffers for _, split in ipairs(splits) do if not split.bufid or not vim.api.nvim_buf_is_loaded(split.bufid) then - local conf = config.get_config() + local conf = config.values local use_local = conf.use_local_fs and split.pos == "right" and utils.in_pr_branch(self.pull_request.bufnr) -- create buffer @@ -469,7 +469,7 @@ end function M._configure_buffer(bufid) utils.apply_mappings("review_diff", bufid) - -- local conf = config.get_config() + -- local conf = config.values -- vim.cmd(string.format("nnoremap %s :OctoAddReviewComment", conf.mappings.review_thread.add_comment)) -- vim.cmd(string.format("vnoremap %s :OctoAddReviewComment", conf.mappings.review_thread.add_comment)) -- vim.cmd(string.format("nnoremap %s :OctoAddReviewSuggestion", conf.mappings.review_thread.add_suggestion)) @@ -477,7 +477,7 @@ function M._configure_buffer(bufid) end function M._detach_buffer(bufid) - local conf = config.get_config() + local conf = config.values for _, lhs in pairs(conf.mappings.review_diff) do pcall(vim.keymap.del, "n", lhs, { buffer = bufid }) end diff --git a/lua/octo/reviews/file-panel.lua b/lua/octo/reviews/file-panel.lua index af859d99..4e3e002c 100644 --- a/lua/octo/reviews/file-panel.lua +++ b/lua/octo/reviews/file-panel.lua @@ -58,7 +58,7 @@ FilePanel.bufopts = { ---@param files FileEntry[] ---@return FilePanel function FilePanel:new(files) - local conf = config.get_config() + local conf = config.values local this = { files = files, size = conf.file_panel.size, @@ -96,7 +96,7 @@ function FilePanel:open() return end - local conf = config.get_config() + local conf = config.values self.size = conf.file_panel.size --vim.cmd("wincmd H") --vim.cmd("vsp") @@ -233,7 +233,7 @@ function FilePanel:render() end local current_review = require("octo.reviews").get_current_review() - local conf = config.get_config() + local conf = config.values local strlen = vim.fn.strlen local s = "Files changed" add_hl("OctoFilePanelTitle", line_idx, 0, #s) diff --git a/lua/octo/reviews/init.lua b/lua/octo/reviews/init.lua index 4d512107..e7950787 100644 --- a/lua/octo/reviews/init.lua +++ b/lua/octo/reviews/init.lua @@ -130,7 +130,7 @@ end function Review:initiate(opts) opts = opts or {} local pr = self.pull_request - local conf = config.get_config() + local conf = config.values if conf.use_local_fs and not utils.in_pr_branch(pr.bufnr) then local choice = vim.fn.confirm("Currently not in PR branch, would you like to checkout?", "&Yes\n&No", 2) if choice == 1 then @@ -224,7 +224,7 @@ function Review:collect_submit_info() return end - local conf = config.get_config() + local conf = config.values local winid, bufnr = window.create_centered_float { header = string.format( "Press %s to approve, %s to comment or %s to request changes", diff --git a/lua/octo/reviews/renderer.lua b/lua/octo/reviews/renderer.lua index 8432f885..bbb7d87f 100644 --- a/lua/octo/reviews/renderer.lua +++ b/lua/octo/reviews/renderer.lua @@ -84,7 +84,7 @@ function M.get_git_hl(status) end function M.get_file_icon(name, ext, render_data, line_idx, offset) - local use_icons = config.get_config().file_panel.use_icons + local use_icons = config.values.file_panel.use_icons if not use_icons then return " " end diff --git a/lua/octo/ui/bubbles.lua b/lua/octo/ui/bubbles.lua index 8169c1cc..d431dbcc 100644 --- a/lua/octo/ui/bubbles.lua +++ b/lua/octo/ui/bubbles.lua @@ -9,7 +9,7 @@ local colors = require "octo.ui.colors" local function make_bubble(content, highlight_group, opts) opts = opts or {} - local conf = config.get_config() + local conf = config.values local margin = string.rep(" ", opts.margin_width or 0) local right_margin = string.rep(" ", opts.right_margin_width or 0) local left_margin = string.rep(" ", opts.left_margin_width or 0) @@ -38,7 +38,7 @@ end local function make_user_bubble(name, is_viewer, opts) opts = opts or {} - local conf = config.get_config() + local conf = config.values local highlight = is_viewer and "OctoUserViewer" or "OctoUser" local icon_position = opts.icon_position or "left" local icon = conf.user_icon @@ -53,7 +53,7 @@ local function make_user_bubble(name, is_viewer, opts) end local function make_reaction_bubble(icon, includes_viewer, opts) - local conf = config.get_config() + local conf = config.values local highlight = includes_viewer and "OctoReactionViewer" or "OctoReaction" local hint_for_viewer = includes_viewer and conf.reaction_viewer_hint_icon or "" local content = icon .. hint_for_viewer diff --git a/lua/octo/ui/colors.lua b/lua/octo/ui/colors.lua index ffa3a017..b81fc714 100644 --- a/lua/octo/ui/colors.lua +++ b/lua/octo/ui/colors.lua @@ -22,7 +22,7 @@ local function get_fg(hl_group_name) end local function get_colors() - local conf = config.get_config() + local conf = config.values return conf.colors end diff --git a/lua/octo/ui/signs.lua b/lua/octo/ui/signs.lua index 1d9ead5c..8d89bfd8 100644 --- a/lua/octo/ui/signs.lua +++ b/lua/octo/ui/signs.lua @@ -3,7 +3,7 @@ local config = require "octo.config" local M = {} function M.setup() - local conf = config.get_config() + local conf = config.values vim.cmd(string.format("sign define octo_thread text=%s texthl=OctoBlue", conf.comment_icon)) vim.cmd(string.format("sign define octo_thread_resolved text=%s texthl=OctoGreen", conf.comment_icon)) diff --git a/lua/octo/ui/writers.lua b/lua/octo/ui/writers.lua index f1a7e490..9147023d 100644 --- a/lua/octo/ui/writers.lua +++ b/lua/octo/ui/writers.lua @@ -466,7 +466,7 @@ function M.write_comment(bufnr, comment, kind, line) ---- PullRequestComment (regular comment (not associated to any review) to a PR review comment) local buffer = octo_buffers[bufnr] - local conf = config.get_config() + local conf = config.values -- heading line = line or vim.api.nvim_buf_line_count(bufnr) + 1 @@ -598,7 +598,7 @@ function M.write_comment(bufnr, comment, kind, line) end local function find_snippet_range(diffhunk_lines) - local conf = config.get_config() + local conf = config.values local context_lines = conf.snippet_context_lines or 4 local snippet_start local count = 0 @@ -818,7 +818,7 @@ end function M.write_review_thread_header(bufnr, opts, line) line = line or vim.api.nvim_buf_line_count(bufnr) - 1 - local conf = config.get_config() + local conf = config.values -- clear virtual texts vim.api.nvim_buf_clear_namespace(bufnr, constants.OCTO_THREAD_HEADER_VT_NS, line, line + 2) @@ -1018,7 +1018,7 @@ end function M.write_issue_summary(bufnr, issue, opts) opts = opts or {} - local conf = config.get_config() + local conf = config.values local max_length = opts.max_length or 80 local chunks = {} @@ -1101,7 +1101,7 @@ function M.write_assigned_event(bufnr, item) -- item.actor.login == vim.g.octo_viewer -- ) local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) --vim.list_extend(vt, actor_bubble) @@ -1114,7 +1114,7 @@ end function M.write_commit_event(bufnr, item) local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) if item.commit.committer.user ~= vim.NIL then @@ -1142,7 +1142,7 @@ function M.write_merged_event(bufnr, item) -- item.actor.login == vim.g.octo_viewer -- ) local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) --vim.list_extend(vt, actor_bubble) @@ -1161,7 +1161,7 @@ function M.write_closed_event(bufnr, item) -- item.actor.login == vim.g.octo_viewer -- ) local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) --vim.list_extend(vt, actor_bubble) @@ -1186,7 +1186,7 @@ function M.write_labeled_events(bufnr, items, action) for _, actor in ipairs(vim.tbl_keys(labels_by_actor)) do local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) --vim.list_extend(vt, actor_bubble) @@ -1212,7 +1212,7 @@ function M.write_reopened_event(bufnr, item) -- item.actor.login == vim.g.octo_viewer -- ) local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) --vim.list_extend(vt, actor_bubble) @@ -1229,7 +1229,7 @@ function M.write_review_requested_event(bufnr, item) -- ) local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) --vim.list_extend(vt, actor_bubble) @@ -1250,7 +1250,7 @@ function M.write_review_request_removed_event(bufnr, item) -- item.actor.login == vim.g.octo_viewer -- ) local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) --vim.list_extend(vt, actor_bubble) @@ -1271,7 +1271,7 @@ function M.write_review_dismissed_event(bufnr, item) -- item.actor.login == vim.g.octo_viewer -- ) local vt = {} - local conf = config.get_config() + local conf = config.values table.insert(vt, { conf.timeline_marker .. " ", "OctoTimelineMarker" }) table.insert(vt, { "EVENT: ", "OctoTimelineItemHeading" }) --vim.list_extend(vt, actor_bubble) diff --git a/lua/octo/utils.lua b/lua/octo/utils.lua index 2b413757..17bfb2b6 100644 --- a/lua/octo/utils.lua +++ b/lua/octo/utils.lua @@ -170,7 +170,7 @@ function M.parse_remote_url(url, aliases) end function M.parse_git_remote() - local conf = config.get_config() + local conf = config.values local aliases = conf.ssh_aliases local job = Job:new { command = "git", args = { "remote", "-v" }, cwd = vim.fn.getcwd() } job:sync() @@ -192,7 +192,7 @@ function M.parse_git_remote() end function M.get_remote() - local conf = config.get_config() + local conf = config.values local remotes = M.parse_git_remote() for _, name in ipairs(conf.default_remote) do if remotes[name] then @@ -1271,7 +1271,7 @@ end --- Apply mappings to a buffer function M.apply_mappings(kind, bufnr) local mappings = require "octo.mappings" - local conf = config.get_config() + local conf = config.values for action, value in pairs(conf.mappings[kind]) do if not M.is_blank(value) diff --git a/lua/tests/octo/config_spec.lua b/lua/tests/octo/config_spec.lua new file mode 100644 index 00000000..b29f5d9b --- /dev/null +++ b/lua/tests/octo/config_spec.lua @@ -0,0 +1,146 @@ +local config = require "octo.config" + +describe("Octo config", function() + before_each(function() + config.values = config.get_default_values() + end) + describe("validation", function() + describe("for bad configs", function() + it("should return invalid when the base config isn't a table", function() + config.values = "INVALID" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when picker isn't a string", function() + config.values.picker = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when picker isn't valid", function() + config.values.picker = "other" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when picker_config isn't a table", function() + config.values.picker_config = "cfg" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when picker_config.mappings isn't a table", function() + config.values.picker_config.mappings = "cfg" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when user_icon isn't a string", function() + config.values.user_icon = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when comment_icon isn't a string", function() + config.values.comment_icon = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when reaction_viewer_hint_icon isn't a string", function() + config.values.reaction_viewer_hint_icon = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when outdated_icon isn't a string", function() + config.values.outdated_icon = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when resolved_icon isn't a string", function() + config.values.resolved_icon = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when timeline_marker isn't a string", function() + config.values.timeline_marker = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when timeline_indent isn't a string", function() + config.values.timeline_indent = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when right_bubble_delimiter isn't a string", function() + config.values.right_bubble_delimiter = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when left_bubble_delimiter isn't a string", function() + config.values.left_bubble_delimiter = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when github_hostname isn't a string", function() + config.values.github_hostname = false + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when use_local_fs isn't a boolean", function() + config.values.use_local_fs = "not a boolean" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when enable_builtin isn't a boolean", function() + config.values.enable_builtin = "not a boolean" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when snippet_context_lines isn't a number", function() + config.values.snippet_context_lines = "not a number" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when gh_env isn't a table", function() + config.values.gh_env = "not a table" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when timeout isn't a number", function() + config.values.timeout = "not a number" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when ui isn't a table", function() + config.values.ui = "not a table" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when issues isn't a table", function() + config.values.issues = "not a table" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when pull_requests isn't a table", function() + config.values.pull_requests = "not a table" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when file_panel isn't a table", function() + config.values.file_panel = "not a table" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when colors isn't a table", function() + config.values.colors = "not a table" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + + it("should return invalid when mappings isn't a table", function() + config.values.mappings = "not a table" + assert.True(vim.tbl_count(require("octo.config").validate_config()) ~= 0) + end) + end) + + describe("for good configs", function() + it("should return valid for the default config", function() + assert.True(vim.tbl_count(require("octo.config").validate_config()) == 0) + end) + end) + end) +end) diff --git a/lua/tests/plenary/config_spec.lua b/lua/tests/plenary/config_spec.lua index 37370e2d..328bd3fa 100644 --- a/lua/tests/plenary/config_spec.lua +++ b/lua/tests/plenary/config_spec.lua @@ -20,7 +20,8 @@ local user_config = { }, }, } -local merged_config = this.setup(user_config) +this.setup(user_config) +local merged_config = this.values describe("Config module:", function() describe("setup", function() -------------------------------------------------- @@ -37,7 +38,6 @@ describe("Config module:", function() it("user defined mappings completely overrides defaults.", function() eq(merged_config.mappings.issue.close_issue.lhs, "") eq(merged_config.mappings.issue.close_issue.desc, "Close issue") - eq(merged_config.mappings.issue.reopen_issue, nil) eq(merged_config.mappings.pull_request.merge_pr.lhs, "pm") eq(merged_config.mappings.pull_request.merge_pr.desc, "merge commit PR") end)