test-ido-completing-read+.el 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. ;;; -*- lexical-binding: t -*-
  2. (require 'undercover)
  3. (undercover "*.el"
  4. (:exclude "test-*.el"))
  5. (require 'ido)
  6. (require 'minibuf-eldef)
  7. (require 'ido-completing-read+)
  8. (require 'buttercup)
  9. (require 'cl-lib)
  10. (require 'with-simulated-input)
  11. (defun collection-as-function (collection)
  12. "Return a function equivalent to COLLECTION.
  13. The returned function will work equivalently to COLLECTION when
  14. passed to `all-completions' and `try-completion'."
  15. (completion-table-dynamic (lambda (string) (all-completions string collection))))
  16. (defun shadow-var (var &optional temp-value)
  17. "Shadow the value of VAR.
  18. This will push the current value of VAR to VAR's
  19. `shadowed-values' property, and then set it to TEMP-VALUE. To
  20. reverse this process, call `unshadow-var' on VAR. Vars can
  21. be shadowed recursively, and must be unshadowed once for each
  22. shadowing in order to restore the original value. You can think
  23. of shadowing as dynamic binding with `let', but with manual
  24. control over when bindings start and end.
  25. If VAR is a Custom variable (see `custom-variable-p'), it will be
  26. set using `customize-set-variable', and if TEMP-VALUE is nil it
  27. will be replaces with VAR's standard value.
  28. Other variables will be set with `set-default', and a TEMP-VALUE
  29. of nil will not be treated specially.
  30. `shadow-var' only works on variables declared as special (i.e.
  31. using `defvar' or similar). It will not work on lexically bound
  32. variables."
  33. (unless (special-variable-p var)
  34. (error "Cannot shadow lexical var `%s'" var))
  35. (let* ((use-custom (custom-variable-p var))
  36. (setter (if use-custom 'customize-set-variable 'set-default))
  37. (temp-value (or temp-value
  38. (and use-custom
  39. (eval (car (get var 'standard-value)))))))
  40. ;; Push the current value on the stack
  41. (push (symbol-value var) (get var 'shadowed-values))
  42. (funcall setter var temp-value)))
  43. (defun var-shadowed-p (var)
  44. "Return non-nil if VAR is shadowed by `shadow-var'."
  45. ;; We don't actually want to return that list if it's non-nil.
  46. (and (get var 'shadowed-values) t))
  47. (defun unshadow-var (var)
  48. "Reverse the last call to `shadow-var' on VAR."
  49. (if (var-shadowed-p var)
  50. (let* ((use-custom (custom-variable-p var))
  51. (setter (if use-custom 'customize-set-variable 'set-default))
  52. (value (pop (get var 'shadowed-values))))
  53. (funcall setter var value))
  54. (error "Var is not shadowed: %s" var)))
  55. (defun fully-unshadow-var (var)
  56. "Reverse *all* calls to `shadow-var' on VAR."
  57. (when (var-shadowed-p var)
  58. (let* ((use-custom (custom-variable-p var))
  59. (setter (if use-custom 'customize-set-variable 'set-default))
  60. (value (car (last (get var 'shadowed-values)))))
  61. (put var 'shadowed-values nil)
  62. (funcall setter var value))))
  63. (defun fully-unshadow-all-vars (&optional vars)
  64. "Reverse *all* calls to `shadow-var' on VARS.
  65. If VARS is nil, unshadow *all* variables."
  66. (if vars
  67. (mapc #'fully-unshadow-var vars)
  68. (mapatoms #'fully-unshadow-var))
  69. nil)
  70. (defmacro shadow-vars (varlist)
  71. "Shadow a list of vars with new values.
  72. VARLIST describes the variables to be shadowed with the same
  73. syntax as `let'.
  74. See `shadow-var'."
  75. (declare (indent 0))
  76. (cl-loop
  77. with var = nil
  78. with value = nil
  79. for binding in varlist
  80. if (symbolp binding)
  81. do (setq var binding
  82. value nil)
  83. else
  84. do (setq var (car binding)
  85. value (cadr binding))
  86. collect `(shadow-var ',var ,value) into exprs
  87. finally return `(progn ,@exprs)))
  88. (defmacro unshadow-vars (vars)
  89. "Un-shadow a list of VARS.
  90. This is a macro for consistency with `shadow-vars', but it will
  91. also accept a quoted list for the sake of convenience."
  92. (declare (indent 0))
  93. (when (eq (car vars) 'quote)
  94. (setq vars (eval vars)))
  95. `(mapc #'unshadow-var ',vars))
  96. (describe "Within the `ido-completing-read+' package"
  97. ;; Reset all of these variables to their standard values before each
  98. ;; test, saving the previous values for later restoration.
  99. (before-each
  100. (shadow-vars
  101. ((ido-mode t)
  102. (ido-ubiquitous-mode t)
  103. (ido-cr+-debug-mode t)
  104. ido-cr+-auto-update-blacklist
  105. ido-cr+-fallback-function
  106. ido-cr+-max-items
  107. ido-cr+-function-blacklist
  108. ido-cr+-function-whitelist
  109. ido-cr+-nil-def-alternate-behavior-list
  110. ido-cr+-replace-completely
  111. ido-confirm-unique-completion
  112. ido-enable-flex-matching
  113. ido-enable-dot-prefix
  114. (minibuffer-electric-default-mode t)))
  115. ;; Suppress all messages during tests
  116. (spy-on 'message))
  117. ;; Restore the saved values after each test
  118. (after-each
  119. (fully-unshadow-all-vars))
  120. (describe "the `ido-completing-read+' function"
  121. (it "should complete with a matching item on RET"
  122. (expect
  123. (with-simulated-input "g RET"
  124. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  125. :to-equal "green"))
  126. (it "should complete with the first match when multiple matches are available"
  127. (expect
  128. (with-simulated-input "b RET"
  129. (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))
  130. :to-equal "brown"))
  131. (it "should allow <left> and <right> to cycle completions, with wrap-around"
  132. (expect
  133. (with-simulated-input "b <right> <right> <right> <right> <left> RET"
  134. (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))
  135. :to-equal
  136. "blue"))
  137. (it "should return \"\" when RET or C-j is pressed on an empty input even when REQUIRE-MATCH is non-nil"
  138. ;; No REQUIRE-MATCH
  139. (expect
  140. (with-simulated-input "RET"
  141. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  142. :to-equal "blue")
  143. (expect
  144. (with-simulated-input "C-j"
  145. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  146. :to-equal "")
  147. ;; Again, with REQUIRE-MATCH
  148. (expect
  149. (with-simulated-input "RET"
  150. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))
  151. :to-equal "")
  152. (expect
  153. (with-simulated-input "C-j"
  154. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))
  155. :to-equal ""))
  156. (it "should accept all the same forms of DEF as `completing-read-default'"
  157. ;; DEF in COLLECTION
  158. (expect
  159. (with-simulated-input "RET"
  160. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "green"))
  161. :to-equal "green")
  162. ;; Same, with REQUIRE-MATCH
  163. (expect
  164. (with-simulated-input "RET"
  165. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "green"))
  166. :to-equal "green")
  167. ;; DEF not in COLLECTION
  168. (expect
  169. (with-simulated-input "RET"
  170. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "brown"))
  171. :to-equal "brown")
  172. ;; Same, with REQUIRE-MATCH
  173. (expect
  174. (with-simulated-input "RET"
  175. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "brown"))
  176. :to-equal "brown")
  177. ;; List DEF, partially in COLLECTION
  178. (expect
  179. (with-simulated-input "RET"
  180. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil '("brown" "green")))
  181. :to-equal "brown"))
  182. (it "should work with INITIAL-INPUT"
  183. (expect
  184. (with-simulated-input "RET"
  185. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr"))
  186. :to-equal "green"))
  187. (it "should properly handle a cons INITIAL-INPUT"
  188. (expect
  189. (with-simulated-input "ee RET"
  190. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil (cons "gr" 2)))
  191. :to-equal "green"))
  192. (it "should properly handle both INITIAL-INPUT and DEF at the same time"
  193. (expect
  194. (with-simulated-input "RET"
  195. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))
  196. :to-equal "green")
  197. (expect
  198. (with-simulated-input "DEL DEL RET"
  199. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))
  200. :to-equal "blue"))
  201. (it "should work when COLLECTION is a function"
  202. (expect
  203. (with-simulated-input "g RET"
  204. (ido-completing-read+ "Prompt: " (collection-as-function '("blue" "yellow" "green"))))
  205. :to-equal "green"))
  206. (it "should fall back when COLLECTION is empty"
  207. (spy-on 'ido-completing-read :and-call-through)
  208. (expect
  209. (with-simulated-input "g RET"
  210. (ido-completing-read+ "Prompt: " nil))
  211. :to-equal "g")
  212. (expect 'ido-completing-read :not :to-have-been-called))
  213. (it "should replace `ido-completing-read' when `ido-cr+-replace-completely' is non-nil"
  214. (customize-set-variable 'ido-cr+-replace-completely t)
  215. (spy-on 'ido-completing-read+ :and-call-through)
  216. (expect
  217. (with-simulated-input "g RET"
  218. (ido-completing-read "Prompt: " '("blue" "yellow" "green")))
  219. :to-equal "green")
  220. (expect 'ido-completing-read+ :to-have-been-called))
  221. (describe "when `ido-cr+-max-items' is set"
  222. (it "should not trigger a fallback for small collections"
  223. (expect
  224. (with-simulated-input "g RET"
  225. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  226. :to-equal "green"))
  227. (it "should trigger a fallback for large collections"
  228. (expect
  229. ;; With max-items negative, all collections are considered "too
  230. ;; large"
  231. (let ((ido-cr+-max-items -1))
  232. (with-simulated-input "g RET"
  233. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green"))))
  234. :to-equal "g")))
  235. (describe "when REQUIRE-MATCH is non-nil"
  236. (it "should still allow exiting with an empty string if DEF is nil"
  237. (expect
  238. (with-simulated-input "C-j"
  239. (ido-completing-read+
  240. "Prompt: "
  241. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  242. :to-equal ""))
  243. ;; "C-j" should NOT be allowed to return an empty string if
  244. ;; require-match and default are both non-nil.
  245. (it "should not alow exiting with an empty string if DEF is non-nil"
  246. (expect
  247. (with-simulated-input "C-j"
  248. (ido-completing-read+
  249. "Prompt: "
  250. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t nil nil "yellow"))
  251. :to-throw))
  252. (it "shouldn't allow C-j to select an ambiguous match"
  253. (expect
  254. (with-simulated-input "b C-j C-j C-j"
  255. (ido-completing-read+
  256. "Prompt: "
  257. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  258. :to-throw)
  259. ;; First press of C-j should complete to "blue" after the
  260. ;; first b, but then get stuck on the choice for the second b.
  261. (expect
  262. (with-simulated-input "b C-j b C-j C-j C-j"
  263. (ido-completing-read+
  264. "Prompt: "
  265. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
  266. :to-throw))
  267. (it "should allow exiting with an unambiguous match"
  268. (expect
  269. (with-simulated-input "b C-j b C-j e C-j C-j"
  270. (ido-completing-read+
  271. "Prompt: "
  272. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
  273. :to-equal "blueberry")
  274. ;; The "C-j" should complete to "bluegrass" and return, because
  275. ;; `ido-confirm-unique-completion is nil.
  276. (expect
  277. (with-simulated-input "b l u e g C-j"
  278. (ido-completing-read+
  279. "Prompt: "
  280. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  281. :to-equal "bluegrass"))
  282. (it "should require an extra C-j to exit when `ido-confirm-unique-completion' is non-nil"
  283. (setq ido-confirm-unique-completion t)
  284. ;; Now the first "C-j" should complete to "bluegrass" but should
  285. ;; not return.
  286. (expect
  287. (with-simulated-input "b l u e g C-j"
  288. (ido-completing-read+
  289. "Prompt: "
  290. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  291. :to-throw)
  292. ;; The first "C-j" should complete to "bluegrass", and the second
  293. ;; should return.
  294. (expect
  295. (with-simulated-input "b l u e g C-j C-j"
  296. (ido-completing-read+
  297. "Prompt: "
  298. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  299. :to-equal "bluegrass"))
  300. ;; Finally, a test for the expected wrong behavior without
  301. ;; ido-cr+. If ido.el ever fixes this bug, it will cause this test
  302. ;; to fail as a signal that the workaround can be phased out.
  303. (it "should return a non-match when ordinary `ido-completing-read' is used"
  304. (expect
  305. (with-simulated-input "b C-j"
  306. (ido-completing-read
  307. "Prompt: "
  308. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
  309. :to-equal "b")))
  310. (describe "when INHERIT-INPUT-METHOD is non-nil"
  311. (before-each
  312. (spy-on 'ido-completing-read :and-call-through))
  313. (it "should not fall back if `current-input-method' is nil"
  314. (expect
  315. (let ((current-input-method nil))
  316. (with-simulated-input "g RET"
  317. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  318. :to-equal "green"))
  319. (expect 'ido-completing-read :to-have-been-called))
  320. (it "should fall back if `current-input-method' is non-nil"
  321. (expect
  322. (let ((current-input-method 'ucs))
  323. (with-simulated-input "g RET"
  324. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil nil t))
  325. :to-equal "green"))
  326. (expect 'ido-completing-read :not :to-have-been-called)))
  327. (describe "with manual fallback shortcuts"
  328. (it "should not fall back when C-b or C-f is used in the middle of the input"
  329. (expect
  330. ;; C-b/f not at beginning/end of input should not fall back
  331. (with-simulated-input "g C-b C-f RET"
  332. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  333. :to-equal "green"))
  334. (it "should fall back on C-f at end of input"
  335. (expect
  336. ;; C-f at end of input should fall back
  337. (with-simulated-input "g C-f RET"
  338. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  339. :to-equal "g"))
  340. (it "should not fall back from repeated C-b that hits the start of input"
  341. (expect
  342. ;; Repeated C-b should not fall back
  343. (with-simulated-input "g C-b C-b C-b C-b RET"
  344. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  345. :to-equal "green"))
  346. (it "should fall back on C-b at beginning of input (if previous action was not C-b)"
  347. (expect
  348. ;; C-b at beginning of line should fall back (if previous action
  349. ;; was not also C-b)
  350. (with-simulated-input "g C-b x DEL C-b RET"
  351. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
  352. :to-equal "g")))
  353. (describe "with a workaround for an bug with non-nil `ido-enable-dot-prefix'"
  354. ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997
  355. ;; for more information on this bug.
  356. (before-each
  357. (setq ido-enable-dot-prefix t))
  358. (it "should not throw an error when \"\" is in the collection"
  359. (expect
  360. (with-simulated-input "RET"
  361. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  362. :to-equal "")
  363. (expect
  364. (with-simulated-input "a a b RET"
  365. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
  366. :to-equal "aab")))
  367. (describe "with dynamic collections"
  368. (before-all
  369. (setq my-dynamic-collection
  370. (completion-table-dynamic
  371. (lambda (text)
  372. (cond
  373. ;; Sub-completions for "hello"
  374. ((s-prefix-p "hello" text)
  375. '("hello" "hello-world" "hello-everyone" "hello-universe"))
  376. ;; Sub-completions for "goodbye"
  377. ((s-prefix-p "goodbye" text)
  378. '("goodbye" "goodbye-world" "goodbye-everyone" "goodbye-universe"))
  379. ;; General completions
  380. (t
  381. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill")))))))
  382. (after-all
  383. (setq my-dynamic-collection nil))
  384. (before-each
  385. (setq ido-enable-flex-matching t
  386. ido-confirm-unique-completion nil)
  387. (spy-on 'ido-cr+-update-dynamic-collection
  388. :and-call-through))
  389. (it "should allow selection of dynamically-added completions"
  390. (expect
  391. (with-simulated-input "hello- RET"
  392. (ido-completing-read+ "Say something: " my-dynamic-collection))
  393. :to-equal "hello-world")
  394. (expect 'ido-cr+-update-dynamic-collection
  395. :to-have-been-called))
  396. (it "should allow ido flex-matching of dynamically-added completions"
  397. (expect
  398. (with-simulated-input "hello-ld RET"
  399. (ido-completing-read+ "Say something: " my-dynamic-collection))
  400. :to-equal
  401. "hello-world")
  402. (expect 'ido-cr+-update-dynamic-collection
  403. :to-have-been-called))
  404. (it "should do a dynamic update when pressing TAB"
  405. (expect
  406. (with-simulated-input "h TAB -ld RET"
  407. (ido-completing-read+ "Say something: " my-dynamic-collection))
  408. :to-equal
  409. "hello-world")
  410. (expect 'ido-cr+-update-dynamic-collection
  411. :to-have-been-called))
  412. (it "should do a dynamic update when idle"
  413. (expect
  414. (with-simulated-input
  415. '("h"
  416. (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time))
  417. "-ld RET")
  418. (ido-completing-read+ "Say something: " my-dynamic-collection))
  419. :to-equal
  420. "hello-world")
  421. (expect 'ido-cr+-update-dynamic-collection
  422. :to-have-been-called))
  423. (it "should do a dynamic update when there is only one match remaining"
  424. (expect
  425. (with-simulated-input "hell-ld RET"
  426. (ido-completing-read+ "Say something: " my-dynamic-collection))
  427. :to-equal
  428. "hello-world")
  429. (expect 'ido-cr+-update-dynamic-collection
  430. :to-have-been-called))
  431. (it "should not exit with a unique match if new matches are dynamically added"
  432. (expect
  433. (with-simulated-input '("hell TAB -ld RET")
  434. (ido-completing-read+ "Say something: " my-dynamic-collection))
  435. :to-equal
  436. "hello-world")
  437. (expect 'ido-cr+-update-dynamic-collection
  438. :to-have-been-called))
  439. (it "should exit with a match that is still unique after dynamic updating"
  440. (expect
  441. (with-simulated-input '("helic TAB")
  442. (ido-completing-read+ "Say something: " my-dynamic-collection))
  443. :to-equal
  444. "helicopter")
  445. (expect 'ido-cr+-update-dynamic-collection
  446. :to-have-been-called))
  447. (it "should suppress errors raised by dynamic completion updates"
  448. (let ((collection
  449. (completion-table-dynamic
  450. (lambda (text)
  451. (cond
  452. ((equal text "")
  453. '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill"))
  454. (t (error "This collection throws an error on a nonempty prefix"))))))
  455. ;; The test framework uses the debugger to catch error
  456. ;; stack traces, but we want to run this code as if it
  457. ;; was not being debugged.
  458. (debug-on-error nil))
  459. (expect
  460. (with-simulated-input '("hell TAB RET")
  461. (ido-completing-read+ "Say something: " collection))
  462. :to-equal
  463. "hello")))
  464. (it "should respect `ido-restrict-to-matches' when doing dynamic updates"
  465. (assume (version<= "25" emacs-version))
  466. (let ((collection
  467. (list "aaa-ddd-ggg" "aaa-eee-ggg" "aaa-fff-ggg"
  468. "bbb-ddd-ggg" "bbb-eee-ggg" "bbb-fff-ggg"
  469. "ccc-ddd-ggg" "ccc-eee-ggg" "ccc-fff-ggg"
  470. "aaa-ddd-hhh" "aaa-eee-hhh" "aaa-fff-hhh"
  471. "bbb-ddd-hhh" "bbb-eee-hhh" "bbb-fff-hhh"
  472. "ccc-ddd-hhh" "ccc-eee-hhh" "ccc-fff-hhh"
  473. "aaa-ddd-iii" "aaa-eee-iii" "aaa-fff-iii"
  474. "bbb-ddd-iii" "bbb-eee-iii" "bbb-fff-iii"
  475. "ccc-ddd-iii" "ccc-eee-iii" "ccc-fff-iii")))
  476. ;; Test the internal function
  477. (expect
  478. (ido-cr+-apply-restrictions
  479. collection
  480. (list (cons nil "bbb")
  481. (cons nil "eee")))
  482. :to-equal '("bbb-eee-ggg" "bbb-eee-hhh" "bbb-eee-iii"))
  483. ;; First verify it without a dynamic collection
  484. (expect
  485. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  486. (ido-completing-read+
  487. "Pick: " collection nil t nil nil (car collection)))
  488. :to-equal "bbb-eee-ggg")
  489. (expect
  490. (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
  491. (ido-completing-read+
  492. "Pick: " collection nil t nil nil (car collection)))
  493. :to-equal "bbb-eee-ggg")
  494. ;; Now test the same with a dynamic collection
  495. (expect
  496. (with-simulated-input "eee C-SPC bbb C-SPC ggg RET"
  497. (ido-completing-read+
  498. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  499. :to-equal "bbb-eee-ggg")
  500. (expect
  501. (with-simulated-input "eee C-SPC aaa C-u C-SPC ccc C-u C-SPC ggg RET"
  502. (ido-completing-read+
  503. "Pick: " (collection-as-function collection) nil t nil nil (car collection)))
  504. :to-equal "bbb-eee-ggg")))
  505. (it "should not hang or error when cycling matches in `Info-menu' (issue #151)"
  506. (expect
  507. (progn
  508. (ido-ubiquitous-mode 1)
  509. (save-excursion
  510. (info)
  511. (with-simulated-input
  512. '("emacs"
  513. (ido-next-match)
  514. (wsi-simulate-idle-time 5)
  515. (ido-next-match)
  516. (wsi-simulate-idle-time 5)
  517. (ido-next-match)
  518. (wsi-simulate-idle-time 5)
  519. (ido-next-match)
  520. (wsi-simulate-idle-time 5)
  521. "RET")
  522. (command-execute 'Info-menu))))
  523. :not :to-throw)))
  524. (describe "with unusual inputs"
  525. (it "should accept a COLLECTION of symbols"
  526. (expect
  527. (with-simulated-input "g RET"
  528. (ido-completing-read+ "Prompt: " '(blue yellow green)))
  529. :to-equal "green"))
  530. (it "should accept a mix of strings and symbols in COLLECTION"
  531. (expect
  532. (with-simulated-input "g RET"
  533. (ido-completing-read+ "Prompt: " '(blue "yellow" green)))
  534. :to-equal "green"))
  535. (it "should accept symbols in DEF"
  536. (expect
  537. (with-simulated-input "RET"
  538. (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil '(brown "green")))
  539. :to-equal "brown"))
  540. (it "should accept an alist COLLECTION"
  541. (expect
  542. (with-simulated-input "RET"
  543. (ido-completing-read+
  544. "Prompt: "
  545. '(("blue" . blue-value)
  546. ("yellow" . yellow-value)
  547. (green . green-value))
  548. nil nil nil nil "green"))
  549. :to-equal "green"))
  550. (it "should accept a hash table COLLECTION"
  551. (expect
  552. (with-simulated-input "RET"
  553. (let ((collection (make-hash-table)))
  554. (puthash "blue" 'blue-value collection)
  555. (puthash "yellow" 'yellow-value collection)
  556. (puthash 'green 'green-value collection)
  557. (ido-completing-read+ "Prompt: " collection nil nil nil nil "green")))
  558. :to-equal "green"))
  559. (it "should accept an obarray COLLECTION"
  560. (expect
  561. (with-simulated-input "forward-char RET"
  562. (ido-completing-read+ "Prompt: " obarray #'commandp
  563. t nil nil "backward-char"))
  564. :to-equal "forward-char"))))
  565. (describe "ido-ubiquitous-mode"
  566. ;; Set up a test command that calls `completing-read'
  567. (before-all
  568. (setf (symbol-function 'test-command)
  569. (lambda ()
  570. (interactive)
  571. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  572. ;; Delete the test command
  573. (after-all
  574. (setf (symbol-function 'test-command) nil))
  575. ;; Verify that the mode can be activated
  576. (it "should enable itself properly"
  577. (expect
  578. (progn
  579. (ido-ubiquitous-mode 1)
  580. (with-simulated-input "g RET"
  581. (command-execute 'test-command)))
  582. :to-equal "green"))
  583. (it "should disable itself properly"
  584. (expect
  585. (progn
  586. (ido-ubiquitous-mode 0)
  587. (with-simulated-input "g RET"
  588. (command-execute 'test-command)))
  589. :to-equal "g"))
  590. (describe "with `ido-cr+-function-blacklist'"
  591. (before-all
  592. (setf (symbol-function 'blacklisted-command)
  593. (lambda (arg)
  594. (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
  595. arg)
  596. (symbol-function 'blacklisted-function)
  597. (lambda ()
  598. (completing-read "Prompt: " '("blue" "yellow" "green")))
  599. (symbol-function 'cmd-that-calls-blacklisted-function)
  600. (lambda ()
  601. (interactive)
  602. (funcall 'blacklisted-function))
  603. (symbol-function 'blacklisted-collection)
  604. (collection-as-function '("blue" "yellow" "green"))))
  605. (after-all
  606. (setf (symbol-function 'blacklisted-command) nil
  607. (symbol-function 'blacklisted-function) nil
  608. (symbol-function 'cmd-that-calls-blacklisted-function) nil
  609. (symbol-function 'blacklisted-collection) nil))
  610. ;; First verify that they work normally before blacklisting them
  611. (describe "when the specified functions are not blacklisted"
  612. (it "should not affect a non-blacklisted command"
  613. (expect
  614. (with-simulated-input "g RET"
  615. (call-interactively 'blacklisted-command))
  616. :to-equal "green"))
  617. (it "should not affect a non-blacklisted function"
  618. (expect
  619. (with-simulated-input "g RET"
  620. (call-interactively 'cmd-that-calls-blacklisted-function))
  621. :to-equal "green"))
  622. (it "should not affect a non-blacklisted collection"
  623. (expect
  624. (with-simulated-input "g RET"
  625. (ido-completing-read+ "Prompt: " 'blacklisted-collection))
  626. :to-equal "green")))
  627. (describe "when the specified functions are blacklisted"
  628. (before-each
  629. (setq ido-cr+-function-blacklist
  630. (append '(blacklisted-command
  631. blacklisted-function
  632. blacklisted-collection)
  633. ido-cr+-function-blacklist)))
  634. (it "should prevent ido in a blacklisted command"
  635. (expect
  636. (with-simulated-input "g RET"
  637. (call-interactively 'blacklisted-command))
  638. :to-equal "g"))
  639. (it "should prevent ido in a blacklisted function"
  640. (expect
  641. (with-simulated-input "g RET"
  642. (call-interactively 'cmd-that-calls-blacklisted-function))
  643. :to-equal "g"))
  644. (it "should prevent ido with a blacklisted collection"
  645. (expect
  646. (with-simulated-input "g RET"
  647. (ido-completing-read+ "Prompt: " 'blacklisted-collection))
  648. :to-equal "g")))
  649. (describe "when updating ido-cr+"
  650. (before-each
  651. (spy-on 'ido-cr+-update-blacklist :and-call-through))
  652. (it "should update the blacklist when `ido-cr+-auto-update-blacklist' is t"
  653. (assume ido-cr+-function-blacklist)
  654. (let ((orig-blacklist ido-cr+-function-blacklist))
  655. (customize-set-variable 'ido-cr+-auto-update-blacklist t)
  656. (customize-set-variable 'ido-cr+-function-blacklist nil)
  657. (ido-cr+-maybe-update-blacklist)
  658. (expect 'ido-cr+-update-blacklist :to-have-been-called)
  659. (expect ido-cr+-function-blacklist :to-have-same-items-as orig-blacklist)))
  660. (it "should not update the blacklist when `ido-cr+-auto-update-blacklist' is nil"
  661. (assume ido-cr+-function-blacklist)
  662. (let ((orig-blacklist ido-cr+-function-blacklist))
  663. (customize-set-variable 'ido-cr+-auto-update-blacklist nil)
  664. (customize-set-variable 'ido-cr+-function-blacklist nil)
  665. (ido-cr+-maybe-update-blacklist)
  666. (expect 'ido-cr+-update-blacklist :not :to-have-been-called)
  667. (expect ido-cr+-function-blacklist :to-have-same-items-as nil)))
  668. (it "should notify about blacklist updates when `ido-cr+-auto-update-blacklist' is `notify'"
  669. (assume ido-cr+-function-blacklist)
  670. (spy-on 'display-warning)
  671. (let ((orig-blacklist ido-cr+-function-blacklist))
  672. (customize-set-variable 'ido-cr+-auto-update-blacklist 'notify)
  673. (customize-set-variable 'ido-cr+-function-blacklist nil)
  674. (ido-cr+-maybe-update-blacklist)
  675. (expect 'ido-cr+-update-blacklist :not :to-have-been-called)
  676. (expect 'display-warning :to-have-been-called)
  677. (expect ido-cr+-function-blacklist :to-have-same-items-as nil)))))
  678. (describe "with `ido-cr+-function-whitelist'"
  679. (before-all
  680. (setf (symbol-function 'whitelisted-command)
  681. (lambda (arg)
  682. (interactive
  683. (list
  684. (completing-read "Prompt: " '("blue" "yellow" "green"))))
  685. arg)
  686. (symbol-function 'whitelisted-function)
  687. (lambda ()
  688. (completing-read "Prompt: " '("blue" "yellow" "green")))
  689. (symbol-function 'cmd-that-calls-whitelisted-function)
  690. (lambda ()
  691. (interactive)
  692. (funcall 'whitelisted-function))
  693. (symbol-function 'whitelisted-collection)
  694. (lambda (string pred action)
  695. (complete-with-action action '("blue" "yellow" "green") string pred))))
  696. (after-all
  697. (setf (symbol-function 'whitelisted-command) nil
  698. (symbol-function 'whitelisted-function) nil
  699. (symbol-function 'cmd-that-calls-whitelisted-function) nil
  700. (symbol-function 'whitelisted-collection) nil))
  701. (describe "when the whitelist is inactive (i.e. everything is whitelisted)"
  702. (before-each
  703. (setq ido-cr+-function-whitelist nil))
  704. (it "should enable ido in a command"
  705. (expect
  706. (with-simulated-input "g RET"
  707. (call-interactively 'whitelisted-command))
  708. :to-equal "green"))
  709. (it "should enable ido in a function"
  710. (expect
  711. (with-simulated-input "g RET"
  712. (call-interactively 'cmd-that-calls-whitelisted-function))
  713. :to-equal "green"))
  714. (it "should enable ido for a collection"
  715. (expect
  716. (with-simulated-input "g RET"
  717. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  718. :to-equal "green")))
  719. (describe "when the specified functions are whitelisted"
  720. (before-each
  721. (setq ido-cr+-function-whitelist
  722. (append '(whitelisted-command
  723. whitelisted-function
  724. whitelisted-collection)
  725. ido-cr+-function-whitelist)))
  726. (it "should enable ido in a whitelisted command"
  727. (expect
  728. (with-simulated-input "g RET"
  729. (call-interactively 'whitelisted-command))
  730. :to-equal "green"))
  731. (it "should enable ido in a whitelisted function"
  732. (expect
  733. (with-simulated-input "g RET"
  734. (call-interactively 'cmd-that-calls-whitelisted-function))
  735. :to-equal "green"))
  736. (it "should enable ido for a whitelisted collection"
  737. (expect
  738. (with-simulated-input "g RET"
  739. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  740. :to-equal "green")))
  741. (describe "when the whitelist is active but empty (i.e. nothing whitelisted)"
  742. (before-each
  743. (setq ido-cr+-function-whitelist (list nil)))
  744. (it "should prevent ido in a command"
  745. (expect
  746. (with-simulated-input "g RET"
  747. (call-interactively 'whitelisted-command))
  748. :to-equal "g"))
  749. (it "should prevent ido in a function"
  750. (expect
  751. (with-simulated-input "g RET"
  752. (call-interactively 'cmd-that-calls-whitelisted-function))
  753. :to-equal "g"))
  754. (it "should prevent ido for a collection"
  755. (expect
  756. (with-simulated-input "g RET"
  757. (ido-completing-read+ "Prompt: " 'whitelisted-collection))
  758. :to-equal "g"))))
  759. (describe "with `ido-cr+-nil-def-alternate-behavior-list'"
  760. (before-all
  761. (setf (symbol-function 'def-nil-command)
  762. (lambda (arg)
  763. (interactive
  764. (list
  765. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))
  766. arg)
  767. (symbol-function 'def-nil-function)
  768. (lambda ()
  769. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))
  770. (symbol-function 'cmd-that-calls-def-nil-function)
  771. (lambda ()
  772. (interactive)
  773. (funcall 'def-nil-function))
  774. (symbol-function 'def-nil-collection)
  775. (lambda (string pred action)
  776. (complete-with-action action '("blue" "yellow" "green") string pred))))
  777. (after-all
  778. (setf (symbol-function 'def-nil-command) nil
  779. (symbol-function 'def-nil-function) nil
  780. (symbol-function 'cmd-that-calls-def-nil-function) nil
  781. (symbol-function 'def-nil-collection) nil))
  782. (describe "when the specified functions are not in the list"
  783. (before-each
  784. (setq ido-cr+-nil-def-alternate-behavior-list nil))
  785. (it "should use empty string default in a command"
  786. (expect
  787. (with-simulated-input "RET"
  788. (call-interactively 'def-nil-command))
  789. :to-equal ""))
  790. (it "should use empty string default in a function"
  791. (expect
  792. (with-simulated-input "RET"
  793. (call-interactively 'cmd-that-calls-def-nil-function))
  794. :to-equal ""))
  795. (it "should use empty string default for a collection"
  796. (expect
  797. (with-simulated-input "RET"
  798. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  799. :to-equal "")))
  800. (describe "when the specified functions are in the list"
  801. (before-each
  802. (setq ido-cr+-nil-def-alternate-behavior-list
  803. (append '(def-nil-command
  804. def-nil-function
  805. def-nil-collection)
  806. ido-cr+-nil-def-alternate-behavior-list)))
  807. (it "should not use empty string default in a command"
  808. (expect
  809. (with-simulated-input "RET"
  810. (call-interactively 'def-nil-command))
  811. :to-equal "blue"))
  812. (it "should not use empty string default in a function"
  813. (expect
  814. (with-simulated-input "RET"
  815. (call-interactively 'cmd-that-calls-def-nil-function))
  816. :to-equal "blue"))
  817. (it "should not use empty string default for a collection"
  818. (expect
  819. (with-simulated-input "RET"
  820. (ido-completing-read+ "Prompt: " 'def-nil-collection nil t))
  821. :to-equal "blue"))))
  822. ;; Test is currently disabled pending additional information
  823. (xit "should not hang or error when deleting characters in `org-refile' (issue #152)"
  824. (expect
  825. (progn
  826. (ido-ubiquitous-mode 1)
  827. (save-excursion
  828. (with-temp-buffer
  829. (org-mode)
  830. (insert (s-trim "
  831. * Heading 1
  832. ** Subheading 1.1
  833. ** Subheading 1.2
  834. ** Subheading 1.3
  835. * Heading 2
  836. * Heading 3
  837. "))
  838. (goto-char (point-max))
  839. ;; TODO Figure out what else needs to be set up to call
  840. ;; `org-refile'
  841. (with-simulated-input
  842. "Heading DEL DEL DEL DEL DEL RET"
  843. (command-execute 'org-refile)))))
  844. :not :to-throw))))
  845. ;;; test-ido-completing-read+.el ends here