From 6f54a04b9dafb127b74ea76ce9d573d848b1f215 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 19 Dec 2023 09:18:30 +0100 Subject: [PATCH] rspec WIP Working Refactor: scaffold branch popup spec, extract shared contexts Add ruby tests to GHA Add linux to bundle gemlock for CI Don't get deps in CI Needs the project Packadd deps Refactoring test setup Add back cwd to rtp debug empty Use runner temp dir debug Try new name Needs trailing slash Skip this for now Give up Add the minimal init stuff no try this try THIS Pass env here test rel path not clean in ci closer init repo first? More branch spec empty logging wait more test test Put back update gitignore Add debug and run on macos Disconnect bin path bin Something chmod bin try bin Use macos bin Debugging --- .github/workflows/test.yml | 43 +++++++---- .gitignore | 1 + .ruby-version | 1 + Gemfile | 15 ++++ Gemfile.lock | 106 ++++++++++++++++++++++++++ Makefile | 2 +- spec/popups/branch_popup_spec.rb | 125 +++++++++++++++++++++++++++++++ spec/spec_helper.rb | 61 +++++++++++++++ spec/support/context/git.rb | 14 ++++ spec/support/context/nvim.rb | 8 ++ spec/support/dependencies.rb | 23 ++++++ spec/support/helpers.rb | 21 ++++++ spec/support/init.lua | 0 spec/support/neovim_client.rb | 82 ++++++++++++++++++++ tests/init.lua | 16 +--- tests/util/util.lua | 20 +++++ 16 files changed, 507 insertions(+), 31 deletions(-) create mode 100644 .ruby-version create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 spec/popups/branch_popup_spec.rb create mode 100644 spec/spec_helper.rb create mode 100644 spec/support/context/git.rb create mode 100644 spec/support/context/nvim.rb create mode 100644 spec/support/dependencies.rb create mode 100644 spec/support/helpers.rb create mode 100644 spec/support/init.lua create mode 100644 spec/support/neovim_client.rb diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c1b374c3..95d1ac46 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,28 +13,41 @@ jobs: strategy: matrix: release: [stable, nightly] + env: + CI: "1" steps: - uses: actions/checkout@v4 - - name: Install Dependencies - run: | - mkdir -p ~/.local/share/neogit-test/site/pack/plenary.nvim/start - cd ~/.local/share/neogit-test/site/pack/plenary.nvim/start - git clone https://github.com/nvim-lua/plenary.nvim - - mkdir -p ~/.local/share/neogit-test/site/pack/telescope.nvim/start - cd ~/.local/share/neogit-test/site/pack/telescope.nvim/start - git clone https://github.com/nvim-telescope/telescope.nvim - - name: Install Neovim run: | wget https://github.com/neovim/neovim/releases/download/${{ matrix.release }}/nvim-linux64.tar.gz tar -zxf nvim-linux64.tar.gz sudo ln -s $(pwd)/nvim-linux64/bin/nvim /usr/local/bin - - name: Test - continue-on-error: false - env: - ci: "1" + - name: Install Dependencies run: | - make test + mkdir -p ~/.local/share/nvim/site/pack/ + cd ~/.local/share/nvim/site/pack/ + + mkdir -p ./plenary.nvim/start + cd ./plenary.nvim/start + git clone https://github.com/nvim-lua/plenary.nvim + + mkdir -p ./telescope.nvim/start + cd ./telescope.nvim/start + git clone https://github.com/nvim-telescope/telescope.nvim + + # - name: Luassert Test + # continue-on-error: true + # run: | + # make test + + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - name: E2E Test + env: + NVIM_RUBY_LOG_LEVEL: "debug" + run: | + bundle exec rspec diff --git a/.gitignore b/.gitignore index 6bfce671..5e39200e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ doc/tags # Environment .envrc +/tests/.min/* diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..15a27998 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.3.0 diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..da9e0f36 --- /dev/null +++ b/Gemfile @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +ruby File.read(".ruby-version").strip + +source "https://rubygems.org" + +gem "debug" +gem "fuubar" +gem "neovim" +gem "quickfix_formatter" +gem "rspec" +gem "tmpdir" +gem "git" +gem "super_diff" +gem "activesupport" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000..95b6b8e2 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,106 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (7.1.2) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + minitest (>= 5.1) + mutex_m + tzinfo (~> 2.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) + attr_extras (7.1.0) + base64 (0.2.0) + bigdecimal (3.1.5) + concurrent-ruby (1.2.2) + connection_pool (2.4.1) + debug (1.9.1) + irb (~> 1.10) + reline (>= 0.3.8) + diff-lcs (1.5.0) + drb (2.2.0) + ruby2_keywords + fileutils (1.7.2) + fuubar (2.5.1) + rspec-core (~> 3.0) + ruby-progressbar (~> 1.4) + git (1.19.0) + addressable (~> 2.8) + rchardet (~> 1.8) + i18n (1.14.1) + concurrent-ruby (~> 1.0) + io-console (0.7.1) + irb (1.11.0) + rdoc + reline (>= 0.3.8) + minitest (5.20.0) + msgpack (1.7.2) + multi_json (1.15.0) + mutex_m (0.2.0) + neovim (0.9.1) + msgpack (~> 1.1) + multi_json (~> 1.0) + optimist (3.1.0) + patience_diff (1.2.0) + optimist (~> 3.0) + psych (5.1.2) + stringio + public_suffix (5.0.4) + quickfix_formatter (0.1.0) + rspec (>= 3.12.0) + rchardet (1.8.0) + rdoc (6.6.2) + psych (>= 4.0.0) + reline (0.4.1) + io-console (~> 0.5) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.2) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.6) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.1) + ruby-progressbar (1.13.0) + ruby2_keywords (0.0.5) + stringio (3.1.0) + super_diff (0.10.0) + attr_extras (>= 6.2.4) + diff-lcs + patience_diff + tmpdir (0.2.0) + fileutils + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + +PLATFORMS + arm64-darwin-22 + arm64-darwin-23 + x86_64-darwin-20 + x86_64-linux + +DEPENDENCIES + activesupport + debug + fuubar + git + neovim + quickfix_formatter + rspec + super_diff + tmpdir + +RUBY VERSION + ruby 3.3.0p0 + +BUNDLED WITH + 2.4.21 diff --git a/Makefile b/Makefile index eba24cd0..06663f0a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ test: - LUA_PATH="./?.lua" TEST_FILES=$$TEST_FILES NEOGIT_LOG_LEVEL=error NEOGIT_LOG_CONSOLE="sync" GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null NVIM_APPNAME=neogit-test nvim --headless -S "./tests/init.lua" + TEMP_DIR=$$TEMP_DIR TEST_FILES=$$TEST_FILES NEOGIT_LOG_LEVEL=error NEOGIT_LOG_CONSOLE="sync" GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null NVIM_APPNAME=neogit-test nvim --headless -S "./tests/init.lua" lint: selene --config selene/config.toml lua diff --git a/spec/popups/branch_popup_spec.rb b/spec/popups/branch_popup_spec.rb new file mode 100644 index 00000000..ba26b9a2 --- /dev/null +++ b/spec/popups/branch_popup_spec.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe "Branch Popup", :git, :nvim do + describe "Variables" do + describe "branch..description" do + it "can edit branch description" + end + + describe "branch..{merge,remote}" do + it "can set the upstream for current branch" + end + + describe "branch..rebase" do + it "can change rebase setting" + end + + describe "branch..pushRemote" do + it "can change pushRemote for current branch" + end + end + + describe "Actions" do + describe "Checkout branch/revision" do + it "can checkout a local branch" + it "can checkout a remote branch" + it "can checkout a tag" + it "can checkout HEAD" + it "can checkout a commit" + end + + describe "Checkout local branch" do + before { git.branch("new-local-branch").checkout } + + it "can checkout a local branch" do + nvim.feedkeys("bl") + nvim.feedkeys("master") + expect(git.current_branch).to eq "master" + end + + it "creates and checks out a new local branch when choosing a remote" + end + + describe "Checkout recent branch" do + it "can checkout a local branch" + end + + describe "Checkout new branch" do + it "can create and checkout a branch" do + nvim.input("new-branch") + nvim.feedkeys("bc") + nvim.feedkeys("master") + + expect(git.current_branch).to eq "new-branch" + end + + it "replaces spaces with dashes in user input" do + nvim.input("new branch with spaces") + nvim.feedkeys("bc") + nvim.feedkeys("master") + + expect(git.current_branch).to eq "new-branch-with-spaces" + end + + it "lets you pick a base branch" do + git.branch("new-base-branch").checkout + + nvim.input("feature-branch") + nvim.feedkeys("bc") + nvim.feedkeys("master") + + expect(git.current_branch).to eq "feature-branch" + + expect( + git.merge_base("feature-branch", "master").first.sha + ).to eq(git.revparse("master")) + end + end + end + + describe "Checkout new spin-off" do + it "can create and checkout a spin-off branch" + end + + describe "Checkout new worktree" do + it "can create and checkout a worktree" + end + + describe "Create new branch" do + it "can create a new branch" + end + + describe "Create new spin-off" do + it "can create a new spin-off" + + context "when there are uncommitted changes" do + it "checks out the spun-off branch" + end + end + + describe "Create new worktree" do + it "can create a new worktree" + end + + describe "Configure" do + it "Launches the configuration popup" + end + + describe "Rename" do + it "can rename a branch" + end + + describe "reset" do + it "can reset a branch" + end + + describe "delete" do + it "can delete a branch" + end + + describe "pull request" do + # Requires Neovim 0.10 + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 00000000..979e9d77 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +require "tmpdir" +require "git" +require "neovim" +require "debug" +require "active_support/all" + +PROJECT_DIR = File.expand_path(File.join(__dir__, "..")) + +Dir[File.join(File.expand_path("."), "spec", "support", "**", "*.rb")].each { |f| require f } + +# Thread.new do +# loop do +# sleep 10 # seconds +# puts "=" * 80; +# Thread.list.each.with_index { |t, i| puts "== Thread #{i}"; puts t.backtrace } +# end +# end + +# module Neovim +# class Connection +# def self.child(argv) +# argv = argv.include?("--embed") ? argv : argv + ["--embed"] +# +# io = ::IO.popen(argv, "rb+") +# # Process.detach(io.pid) +# +# new(io, io) +# end +# end +# end + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.shared_context_metadata_behavior = :apply_to_host_groups + config.filter_run_when_matching :focus + config.example_status_persistence_file_path = "spec/examples.txt" + config.disable_monkey_patching! + config.warnings = true + config.profile_examples = 10 + config.order = :random + + config.include Helpers + + config.around(:each) do |example| + Dir.mktmpdir do |tmp| + Dir.chdir(tmp) do + Git.init + example.run + end + end + end +end diff --git a/spec/support/context/git.rb b/spec/support/context/git.rb new file mode 100644 index 00000000..a3191723 --- /dev/null +++ b/spec/support/context/git.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +RSpec.shared_context "git", :git do + let(:git) { Git.open(Dir.pwd) } + + before do + system("touch testfile") + + git.config("user.email", "test@example.com") + git.config("user.name", "tester") + git.add("testfile") + git.commit("Initial commit") + end +end diff --git a/spec/support/context/nvim.rb b/spec/support/context/nvim.rb new file mode 100644 index 00000000..4dd80823 --- /dev/null +++ b/spec/support/context/nvim.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +RSpec.shared_context "nvim", :nvim do + let(:nvim) { NeovimClient.new } + + before { nvim.setup } + after { nvim.teardown } +end diff --git a/spec/support/dependencies.rb b/spec/support/dependencies.rb new file mode 100644 index 00000000..491ec299 --- /dev/null +++ b/spec/support/dependencies.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +return if ENV["CI"] + +def dir_name(name) + name.match(/[^\/]+\/(?[^\.]+)/)[:dir_name] +end + +def ensure_installed(name) + tmp = File.join(PROJECT_DIR, "tmp") + Dir.mkdir(tmp) if !Dir.exist?(tmp) + + dir = File.join(tmp, dir_name(name)) + + return if Dir.exist?(dir) && !Dir.empty?(dir) + + puts "Downloading dependency #{name} to #{dir}" + Dir.mkdir(dir) + Git.clone("git@github.com:#{name}.git", dir) +end + +ensure_installed "nvim-lua/plenary.nvim" +ensure_installed "nvim-telescope/telescope.nvim" diff --git a/spec/support/helpers.rb b/spec/support/helpers.rb new file mode 100644 index 00000000..b7bf07fd --- /dev/null +++ b/spec/support/helpers.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Helpers + # def wait_for_expect + # last_error = nil + # success = false + # + # 5.times do + # begin + # yield + # success = true + # break + # rescue RSpec::Expectations::ExpectationNotMetError => e + # last_error = e + # sleep 0.5 + # end + # end + # + # raise last_error if !success && last_error + # end +end diff --git a/spec/support/init.lua b/spec/support/init.lua new file mode 100644 index 00000000..e69de29b diff --git a/spec/support/neovim_client.rb b/spec/support/neovim_client.rb new file mode 100644 index 00000000..26615d50 --- /dev/null +++ b/spec/support/neovim_client.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +class NeovimClient + def initialize + @instance = nil + end + + def setup + @instance = attach_child + + if ENV["CI"] + lua <<~LUA + vim.cmd.runtime("plugin/plenary.vim") + vim.cmd.runtime("plugin/neogit.lua") + LUA + else + # Sets up the runtimepath + runtime_dependencies.each do |dep| + lua "vim.opt.runtimepath:prepend('#{dep}')" + end + end + + lua "vim.opt.runtimepath:prepend('#{PROJECT_DIR}')" + + lua <<~LUA + require("plenary") + require('neogit').setup() + require('neogit').open() + LUA + + sleep(0.025) # Seems to be about right + end + + def teardown + @instance.shutdown + @instance = nil + end + + def print_screen + puts get_lines + end + + def lua(code) + @instance.exec_lua(code, []) + end + + def get_lines + @instance.current.buffer.get_lines(0, -1, true).join("\n") + end + + # Overload vim.fn.input() to prevent blocking. + def input(*args) + lua <<~LUA + local inputs = { #{args.map(&:inspect).join(",")} } + + vim.fn.input = function() + return table.remove(inputs, 1) + end + LUA + end + + # Higher-level user input + def feedkeys(keys, mode: 'm') + @instance.feedkeys( + @instance.replace_termcodes(keys, true, false, true), + mode, + false + ) + end + + def attach_child + if ENV["CI"] + Neovim.attach_child(["nvim", "--embed", "--headless"]) + else + Neovim.attach_child(["nvim", "--embed", "--clean", "--headless"]) + end + end + + def runtime_dependencies + Dir[File.join(PROJECT_DIR, "tmp", "*")].select { Dir.exist? _1 } + end +end diff --git a/tests/init.lua b/tests/init.lua index e6002ca3..fdfb1c8d 100644 --- a/tests/init.lua +++ b/tests/init.lua @@ -1,25 +1,11 @@ local util = require("tests.util.util") -local function ensure_installed(repo) - local name = repo:match(".+/(.+)$") - - local install_path = util.neogit_test_base_dir .. name - - vim.opt.runtimepath:prepend(install_path) - - if not vim.loop.fs_stat(install_path) then - print("* Downloading " .. name .. " to '" .. install_path .. "/'") - vim.fn.system { "git", "clone", "--depth=1", "git@github.com:" .. repo .. ".git", install_path } - end -end - if os.getenv("CI") then vim.opt.runtimepath:prepend(vim.fn.getcwd()) vim.cmd([[runtime! plugin/plenary.vim]]) vim.cmd([[runtime! plugin/neogit.lua]]) else - ensure_installed("nvim-lua/plenary.nvim") - ensure_installed("nvim-telescope/telescope.nvim") + util.ensure_installed("nvim-lua/plenary.nvim", util.neogit_test_base_dir) end require("plenary.test_harness").test_directory( diff --git a/tests/util/util.lua b/tests/util/util.lua index 30fb7c29..b2e06585 100644 --- a/tests/util/util.lua +++ b/tests/util/util.lua @@ -48,4 +48,24 @@ function M.create_temp_dir(suffix) return prefix .. vim.trim(M.system(cmd)) end +function M.ensure_installed(repo, path) + local name = repo:match(".+/(.+)$") + + local install_path = path .. name + + vim.opt.runtimepath:prepend(install_path) + + if not vim.loop.fs_stat(install_path) then + print("* Downloading " .. name .. " to '" .. install_path .. "/'") + vim.fn.system { "git", "clone", "--depth=1", "git@github.com:" .. repo .. ".git", install_path } + + if vim.v.shell_error > 0 then + error(string.format("! Failed to clone plugin: '%s' in '%s'!", name, install_path), + vim.log.levels.ERROR) + end + end + + print(vim.fn.system("ls " ..install_path)) +end + return M