;;; test-pearl-heading.el --- Tests for heading title rendering -*- lexical-binding: t; -*- ;; Copyright (C) 2026 Craig Jennings ;; Author: Craig Jennings ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Tests for the heading title transforms: smart title case ;; (`pearl--title-case'), the identifier prefix (`pearl--heading-with-identifier' ;; / `pearl--strip-identifier-prefix'), and the way `--format-issue-as-org-entry' ;; renders them while keeping `LINEAR-TITLE-SHA256' over the bare displayed ;; title so an unedited heading is a no-op on title sync. ;;; Code: (require 'test-bootstrap (expand-file-name "test-bootstrap.el")) ;;; --title-case (ert-deftest test-pearl-title-case-capitalizes-significant-words () "Significant words are capitalized; minor words mid-title stay lowercase." (should (string= "Fix the Refresh Bug" (pearl--title-case "fix the refresh bug"))) (should (string= "A Tale of Two Cities" (pearl--title-case "a tale of two cities")))) (ert-deftest test-pearl-title-case-edges-capitalize-minor-words () "A minor word that is first or last is still capitalized." (should (string= "Of Mice and Men" (pearl--title-case "of mice and men"))) (should (string= "What Is It For" (pearl--title-case "what is it for")))) (ert-deftest test-pearl-title-case-preserves-existing-uppercase () "A word that already has an uppercase letter (acronym, identifier) is left as-is." (should (string= "API Rate Limits" (pearl--title-case "API rate limits"))) (should (string= "GraphQL and You" (pearl--title-case "GraphQL and you")))) (ert-deftest test-pearl-title-case-boundaries () "Empty, single-word, and extra-whitespace inputs behave." (should (string= "" (pearl--title-case ""))) (should (string= "Bug" (pearl--title-case "bug"))) (should (string= "Fix the Bug" (pearl--title-case "fix the bug")))) (ert-deftest test-pearl-title-case-leaves-inner-punctuation-alone () "Only the first letter is upcased, so an apostrophe or hyphen mid-word is intact." (should (string= "Don't Panic" (pearl--title-case "don't panic"))) (should (string= "Re-run the Task" (pearl--title-case "re-run the task")))) ;;; --heading-with-identifier / --strip-identifier-prefix (ert-deftest test-pearl-heading-identifier-prefix-roundtrips () "Adding then stripping the identifier prefix is the identity." (let ((pearl-show-identifier-in-heading t)) (let ((h (pearl--heading-with-identifier "Fix the Bug" "SE-401"))) (should (string= "SE-401: Fix the Bug" h)) (should (string= "Fix the Bug" (pearl--strip-identifier-prefix h "SE-401")))))) (ert-deftest test-pearl-heading-identifier-prefix-disabled () "With prefixing off, the title is returned unchanged." (let ((pearl-show-identifier-in-heading nil)) (should (string= "Fix the Bug" (pearl--heading-with-identifier "Fix the Bug" "SE-401"))))) (ert-deftest test-pearl-strip-identifier-prefix-empty-or-absent () "Stripping is a no-op when the identifier is empty or not at the front." (should (string= "Fix the Bug" (pearl--strip-identifier-prefix "Fix the Bug" ""))) (should (string= "Fix the Bug" (pearl--strip-identifier-prefix "Fix the Bug" nil))) ;; an identifier that only appears mid-title is not stripped (should (string= "see SE-9: later" (pearl--strip-identifier-prefix "see SE-9: later" "SE-9")))) ;;; rendering on / off (ert-deftest test-pearl-format-heading-defaults-prefix-and-title-case () "By default the heading carries the identifier prefix and a title-cased title." (let ((pearl-show-identifier-in-heading t) (pearl-title-case-headings t)) (let ((out (pearl--format-issue-as-org-entry '(:id "u" :identifier "SE-401" :title "fix the refresh bug" :priority 2 :state (:name "Todo"))))) (should (string-match-p "^\\*\\* TODO \\[#B\\] SE-401: Fix the Refresh Bug$" out))))) (ert-deftest test-pearl-format-heading-both-toggles-off-renders-verbatim () "With both toggles off, the heading is the bracket-stripped raw title, no prefix." (let ((pearl-show-identifier-in-heading nil) (pearl-title-case-headings nil)) (let ((out (pearl--format-issue-as-org-entry '(:id "u" :identifier "SE-401" :title "fix the refresh bug" :priority 2 :state (:name "Todo"))))) (should (string-match-p "^\\*\\* TODO \\[#B\\] fix the refresh bug$" out))))) (ert-deftest test-pearl-format-title-hash-is-over-displayed-title-no-prefix () "The title hash is over the displayed (cased) title without the identifier prefix. That is what makes a fetch + unedited heading a no-op on title sync." (let ((pearl-show-identifier-in-heading t) (pearl-title-case-headings t)) (let ((out (pearl--format-issue-as-org-entry '(:id "u" :identifier "SE-401" :title "fix the refresh bug" :priority 2 :state (:name "Todo"))))) (should (string-match-p (format "^:LINEAR-TITLE-SHA256: +%s$" (secure-hash 'sha256 "Fix the Refresh Bug")) out))))) (provide 'test-pearl-heading) ;;; test-pearl-heading.el ends here