diff --git a/CHANGELOG.md b/CHANGELOG.md
index 58e90f4ca..0663ea002 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -45,6 +45,7 @@
### Bugs fixed
+- Fix `cider-log-kill-appender`'s confirmation message, which had the appender and framework names swapped.
- [#4066](https://github.com/clojure-emacs/cider/pull/4066): Jump to the actual source when clicking a stack frame for a top-level anonymous function (the `deftest` shape), instead of landing in `clojure.core/fn` ([#3157](https://github.com/clojure-emacs/cider/issues/3157)).
- [#4058](https://github.com/clojure-emacs/cider/pull/4058): Signal a helpful error when `cider-javadoc` gets an unresolvable (non-absolute) Javadoc URL from the middleware, instead of silently doing nothing ([#2969](https://github.com/clojure-emacs/cider/issues/2969)).
- [#4054](https://github.com/clojure-emacs/cider/pull/4054): Interrupt every REPL an evaluation is dispatched to. In a `.cljc` buffer `cider-interrupt` previously interrupted only one of the two REPLs ([#4036](https://github.com/clojure-emacs/cider/issues/4036)).
@@ -64,6 +65,7 @@
- Bump the injected `cider-nrepl` to 0.62.0-alpha2, which carries the hardened content-type/slurp middleware backing the rich-content revival below (and no longer double-sends `value` alongside content-typed responses).
- Enable rich content in the REPL by default (`cider-repl-use-content-types` is now t): image results render inline, and results naming external content (files, URLs) render a `[show content]` button that fetches and renders it on demand. The automatic fetching that got the feature disabled back in 0.25 ([#2825](https://github.com/clojure-emacs/cider/issues/2825)) is gone - nothing is transferred until the button is pressed - and the server side is hardened in `cider-nrepl` 0.62 (URL-scheme allowlist, size caps, graceful fetch errors).
+- Rename `cider-log-buffer-clear-p` to `cider-log-buffer-has-content-p` - the old name read as the opposite of its behavior; it remains as an obsolete alias.
- Bump the default `cider-repl-history-size` from 500 to 5000.
- Rename the REPL history browser from `cider-repl-history` to `cider-history` (command, mode and options), so its names no longer clash with the REPL's unrelated input-history settings (`cider-repl-history-file`, `cider-repl-history-size`); the old names keep working as obsolete aliases.
- Color the nREPL messages buffer with theme-aware faces (`nrepl-message-faces`, eight faces inheriting from standard font-lock faces) instead of a hardcoded color list; `nrepl-message-colors` is now obsolete, but still takes precedence when customized.
diff --git a/lisp/cider-log.el b/lisp/cider-log.el
index af3325c77..34f45a011 100644
--- a/lisp/cider-log.el
+++ b/lisp/cider-log.el
@@ -161,10 +161,11 @@ It will not be used if the package hasn't been installed."
"Return S with a bold face."
(when s (propertize (format "%s" s) 'face 'bold)))
-(defun cider-log-buffer-clear-p (&optional buffer)
+(defun cider-log-buffer-has-content-p (&optional buffer)
"Return non-nil if BUFFER is not empty, otherwise nil."
(when-let* ((buffer (get-buffer (or buffer cider-log-buffer))))
(> (buffer-size buffer) 0)))
+(define-obsolete-function-alias 'cider-log-buffer-clear-p 'cider-log-buffer-has-content-p "2.0.0")
(defun cider-log--description-clear-events-buffer ()
"Return the description for the clear-events-buffer action."
@@ -221,7 +222,7 @@ It will not be used if the package hasn't been installed."
(cider-nrepl-send-request callback)))
(defun cider-sync-request:log-update-consumer (framework appender consumer)
- "Add CONSUMER to the APPENDER of FRAMEWORK and call CALLBACK on log events."
+ "Update CONSUMER of the APPENDER of FRAMEWORK."
(thread-first `("op" "cider/log-update-consumer"
"framework" ,(cider-log-framework-id framework)
"appender" ,(cider-log-appender-id appender)
@@ -769,7 +770,7 @@ The KEYS are used to lookup the values and are joined by SEPARATOR."
consumer))
(defun cider-log--event-options ()
- "Return the current log consumer."
+ "Return the filter and pagination options for a log event search."
(nrepl-dict "filters" (cider-log--filters)
"limit" cider-log-pagination-limit
"offset" cider-log-pagination-offset))
@@ -1086,8 +1087,8 @@ the CIDER Inspector and the CIDER stacktrace mode.
(cider-sync-request:log-remove-appender framework appender)
(setq-local cider-log-consumer nil)
(message "Log appender %s removed from the %s framework."
- (cider-log-framework-display-name framework)
- (cider-log-appender-display-name appender)))
+ (cider-log-appender-display-name appender)
+ (cider-log-framework-display-name framework)))
(transient-define-suffix cider-log--do-add-appender (framework appender)
"Add the APPENDER to the log FRAMEWORK."
@@ -1173,7 +1174,7 @@ You can jump to functions and methods directly from the printed stacktrace now."
(transient-define-suffix cider-log-clear-event-buffer (buffer)
"Clear the Cider log events in BUFFER."
:description #'cider-log--description-clear-events-buffer
- :inapt-if-not #'cider-log-buffer-clear-p
+ :inapt-if-not #'cider-log-buffer-has-content-p
(interactive (list cider-log-buffer))
(when (get-buffer buffer)
(with-current-buffer buffer
diff --git a/test/cider-log-tests.el b/test/cider-log-tests.el
index d38c8db0a..58387ec5f 100644
--- a/test/cider-log-tests.el
+++ b/test/cider-log-tests.el
@@ -139,3 +139,321 @@
(expect (nrepl-dict-get filters "level") :to-equal "INFO")
(expect (nrepl-dict-get filters "pattern") :to-equal "foo")
(expect (nrepl-dict-get filters "threads") :to-be nil)))))
+
+(describe "cider-log--set-filters"
+ (it "sets the filter variables from the filters dict"
+ (let (cider-log--end-time-filter
+ cider-log--exceptions-filter
+ cider-log--level-filter
+ cider-log--loggers-filter
+ cider-log--pattern-filter
+ cider-log--start-time-filter
+ cider-log--threads-filter)
+ (cider-log--set-filters (nrepl-dict "level" "INFO"
+ "pattern" "foo"
+ "threads" '("main")))
+ (expect cider-log--level-filter :to-equal "INFO")
+ (expect cider-log--pattern-filter :to-equal "foo")
+ (expect cider-log--threads-filter :to-equal '("main"))
+ (expect cider-log--loggers-filter :to-be nil)))
+
+ (it "leaves the filter variables alone when the filters are nil"
+ (let ((cider-log--level-filter "WARN"))
+ (cider-log--set-filters nil)
+ (expect cider-log--level-filter :to-equal "WARN"))))
+
+(describe "cider-log--bold"
+ (it "returns nil for nil"
+ (expect (cider-log--bold nil) :to-be nil))
+ (it "renders any value with a bold face"
+ (let ((s (cider-log--bold 42)))
+ (expect (substring-no-properties s) :to-equal "42")
+ (expect (get-text-property 0 'face s) :to-equal 'bold))))
+
+(describe "cider-log-buffer-has-content-p"
+ (it "returns nil when the buffer doesn't exist"
+ (expect (cider-log-buffer-has-content-p "*cider-log-tests-no-such-buffer*")
+ :to-be nil))
+ (it "returns nil for an empty buffer and non-nil once it has content"
+ (with-temp-buffer
+ (expect (cider-log-buffer-has-content-p (current-buffer)) :to-be nil)
+ (insert "an event")
+ (expect (cider-log-buffer-has-content-p (current-buffer)) :to-be-truthy))))
+
+(describe "cider-log transient descriptions"
+ (it "shows the current framework name, or n/a"
+ (let ((cider-log-framework-name "Logback"))
+ (expect (substring-no-properties (cider-log--description-set-framework))
+ :to-equal "Select framework Logback"))
+ (let ((cider-log-framework-name nil))
+ (expect (substring-no-properties (cider-log--description-set-framework))
+ :to-equal "Select framework n/a")))
+ (it "shows the current log buffer, or n/a"
+ (let ((cider-log-buffer "*cider-log*"))
+ (expect (substring-no-properties (cider-log--description-set-buffer))
+ :to-equal "Select buffer *cider-log*"))
+ (let ((cider-log-buffer nil))
+ (expect (substring-no-properties (cider-log--description-set-buffer))
+ :to-equal "Select buffer n/a")))
+ (it "describes clearing the current log buffer"
+ (let ((cider-log-buffer "*cider-log*"))
+ (expect (substring-no-properties (cider-log--description-clear-events-buffer))
+ :to-equal "Clear *cider-log* buffer"))))
+
+(defun cider-log-tests--request-get (request key)
+ "Return the value following KEY in the nREPL REQUEST list."
+ (cadr (member key request)))
+
+(describe "cider-log nREPL request construction"
+ :var (framework appender event)
+ (before-each
+ (setq framework (nrepl-dict "id" "logback" "name" "Logback")
+ appender (nrepl-dict "id" "my-appender"
+ "filters" (nrepl-dict "level" "INFO")
+ "size" 100
+ "threshold" 10)
+ event (nrepl-dict "id" "ev-42")))
+
+ (it "cider-sync-request:log-add-appender sends the appender settings"
+ (spy-on 'cider-nrepl-sync-request
+ :and-return-value (nrepl-dict "cider/log-add-appender" appender))
+ (expect (cider-sync-request:log-add-appender framework appender)
+ :to-equal appender)
+ (let ((request (car (spy-calls-args-for 'cider-nrepl-sync-request 0))))
+ (expect (cider-log-tests--request-get request "op")
+ :to-equal "cider/log-add-appender")
+ (expect (cider-log-tests--request-get request "framework") :to-equal "logback")
+ (expect (cider-log-tests--request-get request "appender") :to-equal "my-appender")
+ (expect (cider-log-tests--request-get request "size") :to-equal 100)
+ (expect (cider-log-tests--request-get request "threshold") :to-equal 10)
+ (expect (nrepl-dict-get (cider-log-tests--request-get request "filters") "level")
+ :to-equal "INFO")))
+
+ (it "cider-sync-request:log-clear sends the clear-appender op"
+ (spy-on 'cider-nrepl-sync-request :and-return-value (nrepl-dict))
+ (cider-sync-request:log-clear framework appender)
+ (let ((request (car (spy-calls-args-for 'cider-nrepl-sync-request 0))))
+ (expect (cider-log-tests--request-get request "op")
+ :to-equal "cider/log-clear-appender")
+ (expect (cider-log-tests--request-get request "framework") :to-equal "logback")
+ (expect (cider-log-tests--request-get request "appender") :to-equal "my-appender")))
+
+ (it "cider-sync-request:log-search sends the filters and pagination"
+ (spy-on 'cider-nrepl-sync-request
+ :and-return-value (nrepl-dict "cider/log-search" (list event)))
+ (let ((filters (nrepl-dict "pattern" "foo")))
+ (expect (cider-sync-request:log-search framework appender
+ :filters filters :limit 25 :offset 5)
+ :to-equal (list event))
+ (let ((request (car (spy-calls-args-for 'cider-nrepl-sync-request 0))))
+ (expect (cider-log-tests--request-get request "op")
+ :to-equal "cider/log-search")
+ (expect (cider-log-tests--request-get request "filters") :to-equal filters)
+ (expect (cider-log-tests--request-get request "limit") :to-equal 25)
+ (expect (cider-log-tests--request-get request "offset") :to-equal 5))))
+
+ (it "cider-sync-request:log-inspect-event asks for the event by id"
+ (spy-on 'cider-nrepl-sync-request
+ :and-return-value (nrepl-dict "value" "inspected"))
+ (expect (cider-sync-request:log-inspect-event framework appender event)
+ :to-equal "inspected")
+ (let ((request (car (spy-calls-args-for 'cider-nrepl-sync-request 0))))
+ (expect (cider-log-tests--request-get request "op")
+ :to-equal "cider/log-inspect-event")
+ (expect (cider-log-tests--request-get request "event") :to-equal "ev-42")))
+
+ (it "cider-sync-request:log-format-event includes the print options"
+ (spy-on 'cider-nrepl-sync-request
+ :and-return-value (nrepl-dict "cider/log-format-event" "formatted"))
+ (expect (cider-sync-request:log-format-event framework appender event)
+ :to-equal "formatted")
+ (let ((request (car (spy-calls-args-for 'cider-nrepl-sync-request 0))))
+ (expect (cider-log-tests--request-get request "op")
+ :to-equal "cider/log-format-event")
+ (expect (cider-log-tests--request-get request "event") :to-equal "ev-42")
+ (expect (cider-log-tests--request-get request "nrepl.middleware.print/stream?")
+ :to-equal "1")))
+
+ (it "cider-request:log-add-consumer sends the consumer's filters and the callback"
+ (spy-on 'cider-nrepl-send-request)
+ (let ((consumer (nrepl-dict "filters" (nrepl-dict "level" "INFO"))))
+ (cider-request:log-add-consumer framework appender consumer #'ignore)
+ (let ((args (spy-calls-args-for 'cider-nrepl-send-request 0)))
+ (expect (cider-log-tests--request-get (car args) "op")
+ :to-equal "cider/log-add-consumer")
+ (expect (cider-log-tests--request-get (car args) "framework") :to-equal "logback")
+ (expect (cider-log-tests--request-get (car args) "appender") :to-equal "my-appender")
+ (expect (nrepl-dict-get (cider-log-tests--request-get (car args) "filters") "level")
+ :to-equal "INFO")
+ (expect (cadr args) :to-be #'ignore))))
+
+ (it "cider-sync-request:log-remove-consumer identifies the consumer by id"
+ (spy-on 'cider-nrepl-sync-request :and-return-value (nrepl-dict))
+ (cider-sync-request:log-remove-consumer
+ framework appender (nrepl-dict "id" "my-consumer"))
+ (let ((request (car (spy-calls-args-for 'cider-nrepl-sync-request 0))))
+ (expect (cider-log-tests--request-get request "op")
+ :to-equal "cider/log-remove-consumer")
+ (expect (cider-log-tests--request-get request "consumer") :to-equal "my-consumer"))))
+
+(describe "cider-log appender and consumer accessors"
+ (it "reads the appender size, threshold, filters and consumers"
+ (let* ((consumers (list (nrepl-dict "id" "c1")))
+ (filters (nrepl-dict "level" "INFO"))
+ (appender (nrepl-dict "id" "a1" "size" 100 "threshold" 10
+ "filters" filters "consumers" consumers)))
+ (expect (cider-log-appender-size appender) :to-equal 100)
+ (expect (cider-log-appender-threshold appender) :to-equal 10)
+ (expect (cider-log-appender-filters appender) :to-equal filters)
+ (expect (cider-log-appender-consumers appender) :to-equal consumers)))
+
+ (it "finds a consumer of an appender by its id"
+ (let ((appender (nrepl-dict "consumers" (list (nrepl-dict "id" "c1")
+ (nrepl-dict "id" "c2")))))
+ (expect (cider-log-consumer-id
+ (cider-log-appender-consumer appender (nrepl-dict "id" "c2")))
+ :to-equal "c2")
+ (expect (cider-log-appender-consumer appender (nrepl-dict "id" "nope"))
+ :to-be nil)))
+
+ (it "reads the consumer id and filters"
+ (let* ((filters (nrepl-dict "pattern" "foo"))
+ (consumer (nrepl-dict "id" "c1" "filters" filters)))
+ (expect (cider-log-consumer-id consumer) :to-equal "c1")
+ (expect (cider-log-consumer-filters consumer) :to-equal filters)))
+
+ (it "finds the buffers whose local consumer matches"
+ (let ((consumer (nrepl-dict "id" "c1")))
+ (with-temp-buffer
+ (setq-local cider-log-consumer (nrepl-dict "id" "c1"))
+ (let ((match (current-buffer)))
+ (with-temp-buffer
+ (setq-local cider-log-consumer (nrepl-dict "id" "other"))
+ (expect (cider-log-consumer-buffers consumer)
+ :to-equal (list match))))))))
+
+(describe "cider-log--appender"
+ (it "builds the appender dict from the current settings and filters"
+ (let ((cider-log-appender-id "my-appender")
+ (cider-log-appender-size 50)
+ (cider-log-appender-threshold 20)
+ (cider-log--level-filter "INFO")
+ (cider-log--end-time-filter nil)
+ (cider-log--exceptions-filter nil)
+ (cider-log--loggers-filter nil)
+ (cider-log--pattern-filter nil)
+ (cider-log--start-time-filter nil)
+ (cider-log--threads-filter nil))
+ (let ((appender (cider-log--appender)))
+ (expect (nrepl-dict-get appender "id") :to-equal "my-appender")
+ (expect (nrepl-dict-get appender "size") :to-equal 50)
+ (expect (nrepl-dict-get appender "threshold") :to-equal 20)
+ (expect (nrepl-dict-get (nrepl-dict-get appender "filters") "level")
+ :to-equal "INFO"))))
+ (it "returns nil without an appender id"
+ (let ((cider-log-appender-id nil))
+ (expect (cider-log--appender) :to-be nil))))
+
+(describe "cider-log--consumer"
+ (it "includes the current consumer's id when there is one"
+ (with-temp-buffer
+ (setq-local cider-log-consumer (nrepl-dict "id" "c1"))
+ (expect (nrepl-dict-get (cider-log--consumer) "id") :to-equal "c1")))
+ (it "builds a consumer with only filters otherwise"
+ (with-temp-buffer
+ (setq-local cider-log-consumer nil)
+ (let ((consumer (cider-log--consumer)))
+ (expect (nrepl-dict-get consumer "id") :to-be nil)
+ (expect (nrepl-dict-p (nrepl-dict-get consumer "filters")) :to-be-truthy)))))
+
+(describe "cider-log--event-options"
+ (it "includes the filters and the pagination settings"
+ (let ((cider-log-pagination-limit 100)
+ (cider-log-pagination-offset 5)
+ (cider-log--level-filter "INFO")
+ (cider-log--end-time-filter nil)
+ (cider-log--exceptions-filter nil)
+ (cider-log--loggers-filter nil)
+ (cider-log--pattern-filter nil)
+ (cider-log--start-time-filter nil)
+ (cider-log--threads-filter nil))
+ (let ((options (cider-log--event-options)))
+ (expect (nrepl-dict-get options "limit") :to-equal 100)
+ (expect (nrepl-dict-get options "offset") :to-equal 5)
+ (expect (nrepl-dict-get (nrepl-dict-get options "filters") "level")
+ :to-equal "INFO")))))
+
+(describe "cider-log--insert-events"
+ (it "appends formatted events carrying the event as a text property"
+ (with-temp-buffer
+ (let ((event1 (nrepl-dict "id" "e1" "level" "info" "logger" "l"
+ "message" "one" "thread" "t" "timestamp" "ts1"))
+ (event2 (nrepl-dict "id" "e2" "level" "warn" "logger" "l"
+ "message" "two" "thread" "t" "timestamp" "ts2")))
+ (cider-log--insert-events (current-buffer) (list event1 event2))
+ (expect (buffer-substring-no-properties (point-min) (point-max))
+ :to-equal "ts1 [t] INFO l - one\nts2 [t] WARN l - two\n")
+ (goto-char (point-min))
+ (expect (cider-log-event-at-point) :to-equal event1)
+ (forward-line 1)
+ (expect (cider-log-event-at-point) :to-equal event2)))))
+
+(describe "cider-log-event--pretty-print"
+ :var (framework appender event)
+ (before-each
+ (setq framework (nrepl-dict "id" "logback")
+ appender (nrepl-dict "id" "my-appender")
+ event (nrepl-dict "id" "ev-42")))
+
+ (it "shows the formatted event in the log event popup buffer"
+ (let ((cider-log-event-buffer "*cider-log-event-test*"))
+ (spy-on 'cider-sync-request:log-format-event
+ :and-return-value "{:formatted :event}")
+ (spy-on 'cider-popup-buffer :and-call-fake
+ (lambda (name &rest _) (get-buffer-create name)))
+ (unwind-protect
+ (progn
+ (cider-log-event--pretty-print framework appender event)
+ (with-current-buffer cider-log-event-buffer
+ (expect (buffer-string) :to-equal "{:formatted :event}")
+ (expect (point) :to-equal (point-min))))
+ (kill-buffer "*cider-log-event-test*"))))
+
+ (it "doesn't pop up a buffer when the event can't be formatted"
+ (spy-on 'cider-sync-request:log-format-event :and-return-value nil)
+ (spy-on 'cider-popup-buffer)
+ (cider-log-event--pretty-print framework appender event)
+ (expect 'cider-popup-buffer :not :to-have-been-called)))
+
+(describe "cider-log-event--inspect"
+ (it "renders the inspected event value in the inspector"
+ (spy-on 'cider-sync-request:log-inspect-event :and-return-value "inspected")
+ (spy-on 'cider-inspector--render-value)
+ (cider-log-event--inspect (nrepl-dict "id" "logback")
+ (nrepl-dict "id" "my-appender")
+ (nrepl-dict "id" "ev-42"))
+ (expect 'cider-inspector--render-value :to-have-been-called-with "inspected")))
+
+(describe "cider-log--completion-extra-properties"
+ (it "annotates completion candidates with the given keys, whitespace collapsed"
+ (let* ((props (cider-log--completion-extra-properties '("name")))
+ (annotate (plist-get props :annotation-function))
+ (minibuffer-completion-table
+ (list (list "logback" (nrepl-dict "name" "The Logback\n framework")))))
+ (expect (substring-no-properties (funcall annotate "logback"))
+ :to-equal " - The Logback framework")
+ (expect (funcall annotate "unknown") :to-be nil))))
+
+(describe "cider-log-info"
+ (it "summarizes the buffer, framework, appender and consumer"
+ (spy-on 'message)
+ (with-temp-buffer
+ (setq-local cider-log-consumer (nrepl-dict "id" "c1"))
+ (let ((cider-log-buffer "*cider-log*")
+ (cider-log-framework-name "Logback")
+ (cider-log-appender-id "my-appender"))
+ (cider-log-info)))
+ (expect (substring-no-properties
+ (apply #'format (spy-calls-args-for 'message 0)))
+ :to-equal
+ "Buffer: *cider-log* Framework: Logback Appender: my-appender Consumer: c1")))
diff --git a/test/cider-repl-tests.el b/test/cider-repl-tests.el
index 8a01b6e72..366c3587c 100644
--- a/test/cider-repl-tests.el
+++ b/test/cider-repl-tests.el
@@ -116,6 +116,29 @@
;; Clojure 1.8.0, Java 1.8.0_31
"))))
+(describe "cider-repl--banner"
+ (before-each
+ (setq nrepl-endpoint (list :host "localhost" :port "12345"))
+ (spy-on 'cider--version :and-return-value "2.0.0"))
+
+ (it "uses the Babashka banner when only a Babashka version is available"
+ (spy-on 'cider--clojure-version :and-return-value nil)
+ (spy-on 'cider--babashka-version :and-return-value "1.3.190")
+ (spy-on 'cider--babashka-nrepl-version :and-return-value "0.0.6")
+ (expect (cider-repl--banner) :to-equal
+ ";; Connected to nREPL server - nrepl://localhost:12345
+;; CIDER 2.0.0, babashka.nrepl 0.0.6
+;; Babashka 1.3.190
+"))
+
+ (it "falls back to the basic banner without any version info"
+ (spy-on 'cider--clojure-version :and-return-value nil)
+ (spy-on 'cider--babashka-version :and-return-value nil)
+ (expect (cider-repl--banner) :to-equal
+ ";; Connected to nREPL server - nrepl://localhost:12345
+;; CIDER 2.0.0
+")))
+
(defvar cider-testing-ansi-colors-vector
["black" "red3" "green3" "yellow3" "blue2"
"magenta3" "cyan3" "gray90"]
@@ -666,3 +689,94 @@ PROPERTY should be a symbol of either 'text, 'ansi-context or
(current-buffer) "")
:to-be-truthy)
(expect rendered :to-be nil)))))
+
+(describe "cider-repl--emit-verbatim"
+ (it "inserts the text into the output area with a trailing newline"
+ (with-temp-buffer
+ (cider-repl-reset-markers)
+ (cider-repl--emit-verbatim (current-buffer) "hello")
+ (expect (buffer-substring-no-properties (point-min) (point-max))
+ :to-equal "hello\n")
+ ;; the output-end marker has advanced past the inserted text
+ (expect (marker-position cider-repl-output-end) :to-equal (point-max))))
+
+ (it "doesn't add a second newline when the text already ends in one"
+ (with-temp-buffer
+ (cider-repl-reset-markers)
+ (cider-repl--emit-verbatim (current-buffer) "hello\n")
+ (expect (buffer-substring-no-properties (point-min) (point-max))
+ :to-equal "hello\n")))
+
+ (it "preserves the text properties of the inserted string"
+ (with-temp-buffer
+ (cider-repl-reset-markers)
+ (cider-repl--emit-verbatim (current-buffer) (propertize "styled" 'face 'bold))
+ (expect (get-text-property (point-min) 'face) :to-equal 'bold)))
+
+ (it "starts on a fresh line when BOL is requested mid-line"
+ (with-temp-buffer
+ (insert "mid")
+ (cider-repl-reset-markers)
+ (cider-repl--emit-verbatim (current-buffer) "text" nil t)
+ (expect (buffer-substring-no-properties (point-min) (point-max))
+ :to-equal "mid\ntext\n")))
+
+ (it "inserts the result prefix when SHOW-PREFIX is non-nil"
+ (with-temp-buffer
+ (cider-repl-reset-markers)
+ (let ((cider-repl-result-prefix "=> "))
+ (cider-repl--emit-verbatim (current-buffer) "res" t))
+ (expect (buffer-substring-no-properties (point-min) (point-max))
+ :to-equal "=> res\n"))))
+
+(describe "cider-repl-handle-html"
+ (it "emits the rendered HTML verbatim and allows the prompt"
+ (spy-on 'cider--render-html-string
+ :and-return-value (propertize "rendered" 'face 'bold))
+ (with-temp-buffer
+ (cider-repl-reset-markers)
+ (expect (cider-repl-handle-html "text/html" (current-buffer) "raw")
+ :to-be t)
+ (expect 'cider--render-html-string :to-have-been-called-with "raw")
+ (expect (buffer-substring-no-properties (point-min) (point-max))
+ :to-equal "rendered\n")
+ ;; the faces produced by the HTML renderer survive
+ (expect (get-text-property (point-min) 'face) :to-equal 'bold))))
+
+(describe "cider-repl--display-external-body"
+ (it "inserts the URL as a browse-url button plus a fetch button"
+ (with-temp-buffer
+ (cider-repl-reset-markers)
+ (expect (cider-repl--display-external-body (current-buffer) "file:/tmp/x.html")
+ :to-be t)
+ (expect (buffer-substring-no-properties (point-min) (point-max))
+ :to-equal "file:/tmp/x.html [show content]")
+ ;; activating the URL button opens it in the browser
+ (spy-on 'browse-url)
+ (funcall (get-text-property (point-min) 'action) nil)
+ (expect 'browse-url :to-have-been-called-with "file:/tmp/x.html")
+ ;; activating [show content] triggers the slurp fetch
+ (spy-on 'cider-repl--fetch-external-body)
+ (goto-char (point-min))
+ (search-forward "[show content]")
+ (funcall (get-text-property (match-beginning 0) 'action) nil)
+ (expect 'cider-repl--fetch-external-body
+ :to-have-been-called-with (current-buffer) "file:/tmp/x.html"))))
+
+(describe "cider-repl--fetch-external-body"
+ (it "requests the URL's content via the cider/slurp op"
+ (spy-on 'cider-nrepl-send-request)
+ (spy-on 'cider-repl-handler :and-return-value #'ignore)
+ (cider-repl--fetch-external-body (current-buffer) "file:/tmp/x.html")
+ (expect 'cider-repl-handler :to-have-been-called-with (current-buffer))
+ (expect 'cider-nrepl-send-request :to-have-been-called-with
+ '("op" "cider/slurp" "url" "file:/tmp/x.html") #'ignore)))
+
+(describe "cider-repl-content-type-handler-alist"
+ (it "maps the supported content types to defined handler functions"
+ (expect (mapcar #'car cider-repl-content-type-handler-alist)
+ :to-have-same-items-as
+ '("message/external-body" "image/jpeg" "image/png"
+ "image/svg+xml" "text/html"))
+ (dolist (entry cider-repl-content-type-handler-alist)
+ (expect (functionp (cdr entry)) :to-be-truthy))))
diff --git a/test/integration/integration-tests.el b/test/integration/integration-tests.el
index 472fafdbb..769d38b52 100644
--- a/test/integration/integration-tests.el
+++ b/test/integration/integration-tests.el
@@ -377,7 +377,7 @@ If CLI-COMMAND is nil, then use the default."
(cider-itu-poll-until (not (eq (process-status nrepl-proc) 'run)) 5)
(expect (member (process-status nrepl-proc) '(exit signal))))))))))
- (xit "to shadow" ;; disabled: shadow-cljs 2.20.13 incompatible with nREPL 1.6
+ (it "to shadow"
;; shadow asks user whether they want to open a browser, force to no
(spy-on 'y-or-n-p)
@@ -391,9 +391,9 @@ If CLI-COMMAND is nil, then use the default."
(write-region "{:deps true, :nrepl {:middleware [ikappaki.nrepl-mdlw-log/middleware]}}" nil shadow-cljs-edn)
(write-region "{:deps {ikappaki/nrepl-mdlw-log {:git/sha \"d00fecf9f299ffde90b413751f28c1d2e7b56d17\"
:git/url \"https://github.com/ikappaki/nrepl-mdlw-log.git\"}
- thheller/shadow-cljs {:mvn/version \"2.20.13\"}}}"
+ thheller/shadow-cljs {:mvn/version \"3.4.11\"}}}"
nil deps-edn)
- (write-region "{\"dependencies\":{\"shadow-cljs\": \"^2.20.13\"}}" nil package-json)
+ (write-region "{\"dependencies\":{\"shadow-cljs\": \"^3.4.11\"}}" nil package-json)
(let ((default-directory project-dir))
(message ":npm-install...")
(shell-command "npm install")