ebox/tests/ebox-zero-height-tests.el
Kinneyzhang 79f5bc23d1 feat: add CSS sizing and native text interaction capabilities
Normalize size units and intrinsic sizing across Elisp and native layout. Add help, pointer, hover-style and keymap support with reusable interaction adapters.

Keep content updates local, preserve scroll caches and hover borders, and avoid rebuilding retained plans and ownership metadata for stable geometry.

Validation: make check and native-rust-tests passed; targeted native interaction and scroll publication regressions passed.
2026-09-09 22:25:18 +08:00

140 lines
6.5 KiB
EmacsLisp

;;; ebox-zero-height-tests.el --- Empty layout height contracts -*- lexical-binding: t; -*-
;;; Commentary:
;; Empty content has no line box. Explicit lines and box chrome retain their
;; own extents, including when their inline width is zero.
;;; Code:
(require 'ert)
(require 'ebox-fixtures)
(ert-deftest ebox-zero-height-empty-boxes-and-containers ()
(dolist (dsl '((box) (box "") (box :width (px 20))
(box :bgcolor "#112233") (box :height auto)
(box :height (lh 0))
(box :padding-inline (px 2))
(box (box)) (column) (row)
(column (box) (box ""))
(row (box) (box ""))))
(ert-info ((format "empty DSL: %S" dsl))
(let* ((input (ebox-build dsl))
(root (ebox-test-root input))
(ebox--render-source-index (ebox-test-source-index input))
(rendered (ebox-render input)))
(should (equal rendered ""))
(should (= (ebox-string-height rendered) 0))
(should (= (ebox--total-height root) 0))))))
(ert-deftest ebox-zero-height-retains-content-and-explicit-lines ()
(dolist (case '(((box "x") 1)
((box " ") 1)
((box "x\ny") 2)
((box "\n") 2)
((box :height (lh 1)) 1)
((box :width (px 0) :height (lh 2)) 2)
((box :min-height (lh 2)) 2)
((box :height (lh 0.75)) 0)
((box :height (lh 0) "x") 0)
((box :height (lh 0) :overflow hidden "x") 0)
((box :height (lh 0) :overflow visible "x") 1)
((column :height (lh 0) (box "x")) 0)
((row :height (lh 0) (box "x")) 0)
((column :item-gap (lh 1) (box) (box)) 1)
((column :item-gap (lh 2) (box) (box)) 2)))
(ert-info ((format "line DSL: %S" (car case)))
(let ((rendered (ebox-render (ebox-build (car case)))))
(should (= (ebox-string-height rendered) (cadr case)))))))
(ert-deftest ebox-zero-height-composition-does-not-invent-separators ()
(dolist (dsl '((column (box "A") (box) (box "B"))
(box (box :outer block "A")
(box :outer block)
(box :outer block "B"))))
(should (equal (substring-no-properties (ebox-render (ebox-build dsl)))
"A\nB")))
(should (equal (substring-no-properties
(ebox-render (ebox-build '(row (box "A") (box) (box "B")))))
"AB"))
(should (= (ebox--string-pixel-width
(ebox-render (ebox-build '(row (box "A") (box :width (px 20))
(box "B")))))
(+ 20 (ebox--string-pixel-width "AB"))))
(should (= (ebox-string-height
(ebox-render (ebox-build '(column (box :height (lh 1))
(box :height (lh 1))))))
2)))
(ert-deftest ebox-zero-height-retains-padding-margin-and-border ()
(dolist (case '(((box :padding-block (lh 1)) 2)
((box :width (px 20) :padding-block (lh 1)) 2)
((box :margin-block (lh 1)) 2)
((box :width (px 20) :border-top "red") 1)
((box :width (px 20) :border "red") 1)
((box :width (px 20) :height (lh 0) :border "red") 1)
((box :width (px 20) :padding-block (lh 1) :border "red") 2)))
(ert-info ((format "chrome DSL: %S" (car case)))
(let* ((input (ebox-build (car case)))
(root (ebox-test-root input))
(rendered (ebox-render input)))
(should (= (ebox-string-height rendered) (cadr case)))
(should (= (ebox--total-height root) (cadr case)))))))
(ert-deftest ebox-zero-height-shared-line-representation ()
(should-not (ebox-string-lines ""))
(should (= (ebox-string-height nil) 0))
(should (= (ebox-string-height "") 0))
(should (= (ebox-string-height (ebox-lines-join '(""))) 1))
(should (= (ebox--string-pixel-width (ebox-lines-join '(""))) 0))
(should (= (ebox-string-height (ebox--pixel-blank 0 2)) 2))
(should (equal (ebox--lines-stack-vertical "A" "" "B") "A\nB"))
(should (equal (ebox--lines-concat-horizontal "" "") "")))
(ert-deftest ebox-line-measurement-preserves-display-space-widths ()
(dolist (width '(0 86.4))
(let ((line (propertize " " 'display `(space :width (,width)))))
(should (= (ebox--string-pixel-width line) width))
(should (= (ebox--string-pixel-width (ebox--lines-justify line 240))
240)))))
(ert-deftest ebox-zero-height-retained-surface-empty-content-transitions ()
(dolist (initial '((box :id "root")
(column :id "root" (box :id "child"))))
(with-temp-buffer
(ebox-render-to-buffer (current-buffer) (ebox-build initial))
(should (equal (buffer-string) ""))
(let ((revision (ebox-surface-buffer-revision (current-buffer))))
(dolist (content '("A\nB" "" "C" ""))
(let ((input
(ebox-build
(if (eq (car initial) 'column)
`(column :id "root" (box :id "child" ,content))
`(box :id "root" ,content)))))
(ebox-commit (current-buffer) input)
(should (> (ebox-surface-buffer-revision (current-buffer)) revision))
(setq revision (ebox-surface-buffer-revision (current-buffer)))
(should (equal (buffer-string) content))
(should (equal (buffer-string)
(substring-no-properties (ebox-render input)))))))
(ebox-unmount-buffer (current-buffer)))))
(ert-deftest ebox-zero-height-retained-surface-transition-rollback ()
(dolist (contents '(("" "Added") ("Removed" "")))
(with-temp-buffer
(let ((before (ebox-build `(box :id "root" ,(car contents))))
(after (ebox-build `(box :id "root" ,(cadr contents)))))
(ebox-render-to-buffer (current-buffer) before)
(let ((revision (ebox-surface-buffer-revision (current-buffer)))
(rendered (buffer-string)))
(should-error
(ebox-commit (current-buffer) after
(lambda (_report) (error "Reject height transition"))))
(should (= (ebox-surface-buffer-revision (current-buffer)) revision))
(should (equal-including-properties (buffer-string) rendered))
(ebox-commit (current-buffer) after)
(should (equal (buffer-string) (cadr contents))))
(ebox-unmount-buffer (current-buffer))))))
(provide 'ebox-zero-height-tests)
;;; ebox-zero-height-tests.el ends here