aboutsummaryrefslogtreecommitdiff
path: root/tests/test-auto-dim-config.el
blob: 12435fa059a944835bcabb29a185850570b92453 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
;;; test-auto-dim-config.el --- Tests for the auto-dim-other-buffers config -*- lexical-binding: t; -*-

;;; Commentary:
;; auto-dim-config configures the local auto-dim-other-buffers fork: dim only
;; non-selected windows within Emacs (not the whole frame on focus-out), drop
;; fringe from the dimmed faces to avoid flicker on this non-pgtk build, and
;; enable the global mode.  Guarded with `skip-unless' because the fork lives
;; in ~/code and may be absent on a clean checkout.
;;
;; The vterm dim-integration tests were removed when the terminal engine moved
;; off vterm.  EAT (the current engine) renders in real Emacs faces and uses the
;; `default' face for its background, so terminal buffers dim like any other
;; buffer with no dedicated integration.

;;; Code:

(require 'ert)
(require 'cl-lib)
(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))

(defconst test-auto-dim--fork
  (expand-file-name "~/code/auto-dim-other-buffers.el")
  "Local fork directory the module loads via `:load-path'.")

(ert-deftest test-auto-dim-config-applies-settings ()
  "Normal: loading the module enables the mode with the chosen settings."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (unwind-protect
      (progn
        (should (bound-and-true-p auto-dim-other-buffers-mode))
        (should (null auto-dim-other-buffers-dim-on-focus-out))
        (should (eq t auto-dim-other-buffers-dim-on-switch-to-minibuffer))
        (should-not (assq 'fringe auto-dim-other-buffers-affected-faces)))
    (when (fboundp 'auto-dim-other-buffers-mode)
      (auto-dim-other-buffers-mode -1))))

(defconst test-auto-dim--flat-dimmed-org-faces
  (append (mapcar (lambda (n) (intern (format "org-level-%d" n)))
                  (number-sequence 1 8))
          '(org-link org-tag
            ;; Document header: #+TITLE:, #+AUTHOR:, #+ARCHIVE: and their values.
            org-document-title org-document-info org-document-info-keyword
            org-meta-line
            ;; Inline markup and blocks.
            org-code org-verbatim org-block-begin-line org-block-end-line
            ;; Drawers, properties, planning lines.
            org-drawer org-special-keyword org-property-value org-date
            ;; Tables and the fold indicator.
            org-table org-table-row org-ellipsis))
  "Org faces that must flat-dim to the `auto-dim-other-buffers' face.
These carry structure, not status: nothing about them needs to stay
readable in a window the user is not looking at.  Excluded on purpose are
`org-todo' and `org-priority' (keyword class -- see the -dim variant test
below) and `org-hide' (needs `auto-dim-other-buffers-hide' so folded text
stays hidden).")

(defconst test-auto-dim--flat-dimmed-link-faces
  '(link link-visited)
  "Built-in link faces that must flat-dim, distinct from `org-link'.
They fontify links in help, info, and customize buffers.  Both carry
`:underline t', which survives the relative remap, so a dimmed link still
reads as a link.")

(defconst test-auto-dim--flat-dimmed-superstar-faces
  '(org-superstar-header-bullet org-superstar-item org-superstar-first)
  "org-superstar faces that must flat-dim.
org-superstar puts its own face ahead of the org face beneath, so a heading
star renders as (org-superstar-header-bullet org-level-1) and wins over the
dimmed org-level-1.  Without these, bullets stay lit in an unfocused window
even though every face under them dims.
`org-superstar-leading' is excluded on purpose -- see the test below.")

(defconst test-auto-dim--hide-class-faces
  '(org-hide org-superstar-leading org-indent)
  "Faces whose foreground IS the background colour.
That is what makes them invisible.  They take `auto-dim-other-buffers-hide',
never the flat dim, which would paint them visible grey.")

(defconst test-auto-dim--no-foreground-faces
  '(bold italic underline)
  "Faces that carry no foreground, even through inheritance.
They set weight, slant or underline only, so text wearing them takes its
colour from `default', which is already remapped.  They need no entry and
must not gain one, or the alist grows entries that do nothing.")

(defconst test-auto-dim--keyword-dim-variants
  '((org-faces-todo       . org-faces-todo-dim)
    (org-faces-doing      . org-faces-doing-dim)
    (org-faces-priority-a . org-faces-priority-a-dim))
  "Sample of keyword faces that must keep dedicated -dim variants.")

(ert-deftest test-auto-dim-config-org-structure-faces-flat-dim ()
  "Normal: org heading, link, and tag faces remap to the flat dim face."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (dolist (face test-auto-dim--flat-dimmed-org-faces)
    (let ((entry (assq face auto-dim-other-buffers-affected-faces)))
      (should entry)
      (should (eq 'auto-dim-other-buffers (car (cdr entry))))
      (should (null (cdr (cdr entry)))))))

(ert-deftest test-auto-dim-config-link-faces-flat-dim ()
  "Normal: the built-in `link' and `link-visited' faces flat-dim.
Without these, links in help, info, and customize buffers stay lit while
the rest of an unfocused window fades.  `org-link' is a separate face and
is covered above."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (dolist (face test-auto-dim--flat-dimmed-link-faces)
    (let ((entry (assq face auto-dim-other-buffers-affected-faces)))
      (should entry)
      (should (eq 'auto-dim-other-buffers (car (cdr entry))))
      (should (null (cdr (cdr entry)))))))

(ert-deftest test-auto-dim-config-link-underline-survives-the-remap ()
  "Boundary: the dim face sets no `:underline', so the link cue survives.
A relative remap layers the dim face over the base face, so an underline
the dim face does not specify falls through from `link'.  If the theme ever
gives `auto-dim-other-buffers' an `:underline', dimmed links stop looking
like links and this test says so."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (should (eq 'unspecified
              (face-attribute 'auto-dim-other-buffers :underline nil nil))))

(ert-deftest test-auto-dim-config-superstar-bullets-flat-dim ()
  "Normal: org-superstar's heading stars and list bullets flat-dim.
They were the last thing left lit in an unfocused org window."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (dolist (face test-auto-dim--flat-dimmed-superstar-faces)
    (let ((entry (assq face auto-dim-other-buffers-affected-faces)))
      (should entry)
      (should (eq 'auto-dim-other-buffers (car (cdr entry))))
      (should (null (cdr (cdr entry)))))))

(ert-deftest test-auto-dim-config-superstar-leading-uses-hide-face ()
  "Error: `org-superstar-leading' takes the -hide face, never the flat dim.
Its foreground is the background colour, which is what keeps hidden leading
stars invisible.  Flat-dimming it would give them the dim face's visible grey
and reveal stars the user chose to hide.  Same contract as `org-hide'."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (should-not (memq 'org-superstar-leading
                    test-auto-dim--flat-dimmed-superstar-faces))
  (let ((entry (assq 'org-superstar-leading auto-dim-other-buffers-affected-faces)))
    (should entry)
    (should (eq 'auto-dim-other-buffers-hide (car (cdr entry))))))

(ert-deftest test-auto-dim-config-hide-class-faces-use-hide-face ()
  "Error: every background-coloured face takes the -hide face.
`org-hide', `org-superstar-leading' and `org-indent' all resolve to the
background colour, which is what keeps folded text, leading stars and indent
prefixes invisible.  Flat-dimming any of them reveals what the user hid."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (dolist (face test-auto-dim--hide-class-faces)
    (let ((entry (assq face auto-dim-other-buffers-affected-faces)))
      (should entry)
      (should (eq 'auto-dim-other-buffers-hide (car (cdr entry)))))))

(ert-deftest test-auto-dim-config-no-org-face-left-unmapped ()
  "Boundary: a fontified org buffer uses no face we forgot to handle.
Four rounds of this bug all had the same shape: a face nobody enumerated,
sitting ahead of a mapped face in a face list and outranking it.  This walks
a representative buffer, collects every face it actually uses (including the
`line-prefix' and `wrap-prefix' org-indent hangs its faces on), and fails on
anything that is neither mapped nor deliberately excluded.

Built-in org only.  org-superstar and org-drill are elpa packages, and the
test run has no `package-initialize', so their faces are pinned by name in
the tests above instead."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (require 'org)
  (let ((used (make-hash-table :test #'eq))
        (allowed (append test-auto-dim--no-foreground-faces
                         ;; Keyword class: deliberately unmapped so status stays
                         ;; readable in an unfocused window.  Pinned by
                         ;; test-auto-dim-config-todo-priority-faces-not-flat-dimmed.
                         '(org-todo org-priority)
                         (mapcar #'car auto-dim-other-buffers-affected-faces))))
    (with-temp-buffer
      (insert "#+TITLE: T\n#+AUTHOR: A\n\n* H1 :tag:\n** TODO [#A] task\n"
              "DEADLINE: <2026-07-10 Fri>\n:PROPERTIES:\n:K: v\n:END:\n"
              "Body ~verbatim~ =code= [[https://x.org][link]].\n"
              "| a | b |\n|---+---|\n| 1 | 2 |\n"
              "#+begin_src sh\necho hi\n#+end_src\n"
              "- [X] done item\n")
      (org-mode)
      (font-lock-ensure)
      (let ((p (point-min)))
        (while (< p (point-max))
          (dolist (f (let ((v (get-text-property p 'face)))
                       (if (listp v) v (list v))))
            (when (and f (symbolp f)) (puthash f t used)))
          (dolist (prop '(line-prefix wrap-prefix))
            (let ((s (get-text-property p prop)))
              (when (stringp s)
                (dolist (f (let ((v (get-text-property 0 'face s)))
                             (if (listp v) v (list v))))
                  (when (and f (symbolp f)) (puthash f t used))))))
          (setq p (1+ p)))))
    (let (unmapped)
      (maphash (lambda (face _v)
                 (unless (memq face allowed) (push face unmapped)))
               used)
      (should (equal nil (sort unmapped #'string<))))))

(ert-deftest test-auto-dim-config-keyword-faces-keep-dim-variants ()
  "Boundary: org TODO-keyword faces keep dedicated -dim variants, not flat dim.
Keyword status is scanned across unfocused windows, so it earns a variant;
heading colour does not.  Guards the flat-dim change from over-reaching."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (dolist (pair test-auto-dim--keyword-dim-variants)
    (let ((entry (assq (car pair) auto-dim-other-buffers-affected-faces)))
      (should entry)
      (should (eq (cdr pair) (car (cdr entry)))))))

(ert-deftest test-auto-dim-config-todo-priority-faces-not-flat-dimmed ()
  "Boundary: `org-todo' and `org-priority' are never flat-dimmed.
They are keyword class.  Dimming them would erase the status colour the
-dim variants exist to preserve, so they stay out of the flat-dim set."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (dolist (face '(org-todo org-priority))
    (should-not (memq face test-auto-dim--flat-dimmed-org-faces))
    (let ((entry (assq face auto-dim-other-buffers-affected-faces)))
      (should-not (and entry (eq 'auto-dim-other-buffers (car (cdr entry))))))))

(ert-deftest test-auto-dim-config-org-hide-uses-hide-face ()
  "Boundary: `org-hide' remaps to the -hide face, not the flat dim face.
Flat-dimming it would give folded text a visible foreground."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (let ((entry (assq 'org-hide auto-dim-other-buffers-affected-faces)))
    (should entry)
    (should (eq 'auto-dim-other-buffers-hide (car (cdr entry))))))

(ert-deftest test-auto-dim-config-never-dim-dashboard-exempts-dashboard ()
  "Normal: the *dashboard* buffer is exempt from dimming."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (let* ((existed (get-buffer "*dashboard*"))
         (buffer (or existed (get-buffer-create "*dashboard*"))))
    (unwind-protect
        (should (cj/auto-dim--never-dim-dashboard-p buffer))
      (unless existed (kill-buffer buffer)))))

(ert-deftest test-auto-dim-config-never-dim-dashboard-near-miss-name-dims ()
  "Boundary: a buffer whose name only resembles the dashboard is not exempt."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (let ((buffer (get-buffer-create "dashboard")))
    (unwind-protect
        (should-not (cj/auto-dim--never-dim-dashboard-p buffer))
      (kill-buffer buffer))))

(ert-deftest test-auto-dim-config-never-dim-dashboard-other-buffer-dims ()
  "Error: an ordinary buffer is not exempt from dimming."
  (skip-unless (file-directory-p test-auto-dim--fork))
  (require 'auto-dim-config)
  (with-temp-buffer
    (should-not (cj/auto-dim--never-dim-dashboard-p (current-buffer)))))

(provide 'test-auto-dim-config)
;;; test-auto-dim-config.el ends here