summary line initial version added
This commit is contained in:
@@ -84,10 +84,11 @@ class Phantom:
|
||||
|
||||
|
||||
class Window:
|
||||
"""Captures the quick panel so tests can drive its callbacks"""
|
||||
"""Captures the quick panel and the input panel so tests can drive their callbacks"""
|
||||
|
||||
def __init__(self):
|
||||
self.panel = None
|
||||
self.input = None
|
||||
self.commands = []
|
||||
|
||||
def focus_view(self, view):
|
||||
@@ -99,6 +100,10 @@ class Window:
|
||||
def show_quick_panel(self, items, on_select, flags=0, selected=-1, on_highlight=None, placeholder=None):
|
||||
self.panel = (items, on_select, selected, on_highlight, placeholder)
|
||||
|
||||
def show_input_panel(self, caption, initial_text, on_done, on_change, on_cancel):
|
||||
self.input = (caption, initial_text, on_done, on_change, on_cancel)
|
||||
return View()
|
||||
|
||||
|
||||
class View:
|
||||
"""Text buffer with just the calls render.py makes"""
|
||||
|
||||
@@ -145,3 +145,54 @@ def test_j_and_k_move_the_caret_one_line():
|
||||
("move", {"by": "lines", "forward": True}),
|
||||
("move", {"by": "lines", "forward": False}),
|
||||
]
|
||||
|
||||
|
||||
def staged_state(*flags):
|
||||
st = state.ViewState("/repo")
|
||||
st.files = [git.FileChange("f{}.py".format(i), "M", flag, 1, 1, None) for i, flag in enumerate(flags)]
|
||||
return st
|
||||
|
||||
|
||||
def test_commit_needs_a_staged_file(monkeypatch):
|
||||
messages = []
|
||||
monkeypatch.setattr(commands.sublime, "status_message", lambda m: messages.append(m))
|
||||
window = sublime.Window()
|
||||
|
||||
commands.ACTIONS["commit"](sublime.View(), window, staged_state(False, False))
|
||||
assert messages == ["vDiff: no staged files"] and window.input is None
|
||||
|
||||
# a commit under review has nothing to stage in the first place
|
||||
st = staged_state(True)
|
||||
st.enter("abc", None)
|
||||
commands.ACTIONS["commit"](sublime.View(), window, st)
|
||||
assert messages[-1] == "vDiff: commit works on uncommitted changes only" and window.input is None
|
||||
|
||||
|
||||
def test_commit_prompts_then_commits(monkeypatch):
|
||||
committed = []
|
||||
refreshed = []
|
||||
monkeypatch.setattr(commands.git, "commit", lambda repo, message: committed.append((repo, message)))
|
||||
monkeypatch.setattr(commands, "refresh_listing", lambda view, st, message=None: refreshed.append(message))
|
||||
window = sublime.Window()
|
||||
|
||||
commands.ACTIONS["commit"](sublime.View(), window, staged_state(True, False, True))
|
||||
caption, initial, on_done, _, _ = window.input
|
||||
assert caption == "Commit message" and initial == "" and committed == []
|
||||
|
||||
on_done(" ") # an empty message is not a commit
|
||||
assert committed == []
|
||||
|
||||
on_done("real message")
|
||||
assert committed == [("/repo", "real message")]
|
||||
assert refreshed == ["committed 2 files"]
|
||||
|
||||
|
||||
def test_commit_message_counts_one_file(monkeypatch):
|
||||
refreshed = []
|
||||
monkeypatch.setattr(commands.git, "commit", lambda repo, message: None)
|
||||
monkeypatch.setattr(commands, "refresh_listing", lambda view, st, message=None: refreshed.append(message))
|
||||
window = sublime.Window()
|
||||
|
||||
commands.ACTIONS["commit"](sublime.View(), window, staged_state(True, False))
|
||||
window.input[2]("one")
|
||||
assert refreshed == ["committed 1 file"]
|
||||
|
||||
@@ -146,3 +146,26 @@ def test_count_lines(tmp_path):
|
||||
assert git._count_lines(p) is None
|
||||
open(p, "wb").write(b"")
|
||||
assert git._count_lines(p) == 0
|
||||
|
||||
|
||||
def test_commit_only_records_staged_files(repo):
|
||||
write(repo, "a.py", "staged\n")
|
||||
write(repo, "b.txt", "left alone\n")
|
||||
changes = {c.path: c for c in git.changed_files(repo)}
|
||||
git.stage(repo, changes["a.py"])
|
||||
|
||||
git.commit(repo, "a message with 'quotes' and \"doubles\"")
|
||||
|
||||
left = {c.path: c for c in git.changed_files(repo)}
|
||||
assert "a.py" not in left and not left["b.txt"].staged
|
||||
info, files = git.commit_with_files(repo, "HEAD")
|
||||
# the message reaches git verbatim: it is one argv element, never a shell string
|
||||
assert info.subject == "a message with 'quotes' and \"doubles\""
|
||||
assert [f.path for f in files] == ["a.py"]
|
||||
|
||||
|
||||
def test_commit_without_staged_files_raises(repo):
|
||||
write(repo, "a.py", "unstaged only\n")
|
||||
with pytest.raises(git.GitError) as info:
|
||||
git.commit(repo, "nothing to record")
|
||||
assert info.value.code != 0
|
||||
|
||||
@@ -132,9 +132,9 @@ def test_help_popup_lists_every_key():
|
||||
view, html = help_popup(st)
|
||||
|
||||
assert view.is_popup_visible()
|
||||
for group in ("Navigate", "Panels", "This file", "View"):
|
||||
for group in ("Navigate", "Panels", "Actions", "View"):
|
||||
assert ">{}<".format(group) in html
|
||||
for keys in ("] [", "n p", "o ⏎", "j k", "f", "c", "s", "u", "d", "r", "?", "esc"):
|
||||
for keys in ("] [", "n p", "o ⏎", "j k", "f", "c", "s", "u", "d", "C m", "r", "?", "esc"):
|
||||
assert ">{}<".format(keys) in html
|
||||
assert "next / previous change" in html and "open the real file at this line" in html
|
||||
# working-tree mode: nothing dimmed, every single-action row is a link
|
||||
@@ -152,8 +152,9 @@ def test_help_popup_dims_file_actions_in_commit_mode():
|
||||
_, html = help_popup(st)
|
||||
|
||||
assert "uncommitted only" in html
|
||||
assert html.count('class="dim"') == 3
|
||||
assert html.count('class="dim"') == 4
|
||||
assert 'href="stage"' not in html and 'href="unstage"' not in html and 'href="discard"' not in html
|
||||
assert 'href="commit"' not in html
|
||||
# everything that still applies stays clickable
|
||||
assert 'href="files"' in html and 'href="open"' in html and 'href="refresh"' in html
|
||||
|
||||
@@ -222,3 +223,50 @@ def test_minihtml_stylesheets_reject_unsupported_selectors():
|
||||
head = rule.split("{")[0].strip()
|
||||
for selector in filter(None, (s.strip() for s in head.split(","))):
|
||||
assert not unsupported.search(selector), "{}: {}".format(name, selector)
|
||||
|
||||
|
||||
# --- header summary line ----------------------------------------------------
|
||||
|
||||
|
||||
def test_summary_counts_staged_and_unstaged_files():
|
||||
view = sublime.View()
|
||||
render.init_view(view)
|
||||
st = make_state()
|
||||
st.files = [
|
||||
git.FileChange("a.py", "M", True, 1, 1, None),
|
||||
git.FileChange("b.py", "M", False, 1, 1, None),
|
||||
git.FileChange("c.py", "?", False, 1, 0, None),
|
||||
]
|
||||
render.render(view, st)
|
||||
assert '<div class="summary">1 staged, 2 unstaged</div>' in header(view)
|
||||
|
||||
|
||||
def test_summary_totals_a_commit_and_owns_the_date():
|
||||
view = sublime.View()
|
||||
render.init_view(view)
|
||||
st = make_state()
|
||||
st.enter("abc", git.CommitInfo("abc", "abc1234", "Ann", "2026-09-23 10:00", "Fix things", ""))
|
||||
st.change = git.FileChange("x.py", "M", False, 3, 2, None)
|
||||
st.files = [
|
||||
git.FileChange("x.py", "M", False, 3, 2, None),
|
||||
git.FileChange("y.py", "M", False, 7, 0, None),
|
||||
git.FileChange("logo.png", "M", False, None, None, None), # binary: no counts, still a file
|
||||
]
|
||||
render.render(view, st)
|
||||
html = header(view)
|
||||
|
||||
# hash, author, date, totals and message all live on the one summary line
|
||||
summary = html.split('<div class="summary">')[1].split("</div>")[0]
|
||||
for part in ("abc1234", "Ann", "2026-09-23 10:00", "3 files changed", "-2", "+10", "Fix things"):
|
||||
assert part in summary, part
|
||||
# and nowhere else: there is no second commit line any more
|
||||
assert html.count("abc1234") == 1 and html.count("2026-09-23 10:00") == 1
|
||||
assert 'class="commit"' not in html
|
||||
|
||||
|
||||
def test_summary_is_omitted_without_a_file_list():
|
||||
view = sublime.View()
|
||||
render.init_view(view)
|
||||
st = make_state()
|
||||
render.render(view, st)
|
||||
assert 'class="summary"' not in header(view)
|
||||
|
||||
Reference in New Issue
Block a user