From c593eaf484a6b5fa93dba692d8edf2c2d2e52452 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Wed, 25 Feb 2026 10:25:00 +0200 Subject: [PATCH 1/5] Add script to generate within namespace --- scripts/generate_within.clj | 122 +++++++++++++++++++++++ scripts/within_query_all_fn.template.clj | 8 ++ scripts/within_query_fn.template.clj | 8 ++ 3 files changed, 138 insertions(+) create mode 100644 scripts/generate_within.clj create mode 100644 scripts/within_query_all_fn.template.clj create mode 100644 scripts/within_query_fn.template.clj diff --git a/scripts/generate_within.clj b/scripts/generate_within.clj new file mode 100644 index 0000000..0d87a64 --- /dev/null +++ b/scripts/generate_within.clj @@ -0,0 +1,122 @@ +#!/usr/bin/env bb + +(require '[clojure.string :as str]) + +(def within-source-file "../src/main/react_testing_library_cljs/within.cljs") + +(def begin-marker ";; Begin - Generated Code (Do not modify manually)\n") +(def end-marker ";; End - Generated Code (Do not modify manually)\n") + +(def query-fn-template (slurp "within_query_fn.template.clj")) +(def query-all-fn-template (slurp "within_query_all_fn.template.clj")) + +(defn insert-string [original string position] + (str (subs original 0 position) + string + (subs original position))) + +(defn remove-string-between [string begin end] + (str (subs string 0 begin) + (subs string end))) + +(defn camel-case->kebab-case [string] + (reduce + (fn [result char] + (if (= (str char) (str/upper-case char)) + (str result "-" (str/lower-case char)) + (str result char))) + "" + string)) + +(let [query-types [{:type "getBy" + :docstring (str "Returns the matching element scoped to `element` for a query.\n\n " + "Throws a descriptive error if no elements match or if more than one match is found.\n " + "Use `get-all-by` instead if more than one element is expected.\n\n " + "- No match: Throws error\n " + "- One match: Returns element\n " + "- Multiple match: Throws error\n " + "- Async: No") + :template query-fn-template} + {:type "queryBy" + :docstring (str "Returns the matching element scoped to `element` for a query, or `nil` if no elements match.\n\n " + "Useful for asserting an element that is not present. Throws an error if more than one\n " + "match is found. Use `query-all-by` instead if this is OK.\n\n " + "- No match: Returns nil\n " + "- One match: Returns element\n " + "- Multiple match: Throws error\n " + "- Async: No") + :template query-fn-template} + {:type "findBy" + :docstring (str "Returns a promise which resolves when a matching element is found within `element`.\n\n " + "The promise is rejected if no element is found or if more than one element is found\n " + "after a default timeout of 1000ms. If you need to find more than one element, use\n " + "`find-all-by`. This is a combination of `get-by` queries and `waitFor`.\n\n " + "- No match: Rejects\n " + "- One match: Resolves with element\n " + "- Multiple match: Rejects\n " + "- Async: Yes") + :template query-fn-template} + {:type "getAllBy" + :docstring (str "Returns a vector of all matching elements scoped to `element` for a query.\n\n " + "Throws an error if no elements match.\n\n " + "- No match: Throws error\n " + "- One match: Returns vector\n " + "- Multiple match: Returns vector\n " + "- Async: No") + :template query-all-fn-template} + {:type "queryAllBy" + :docstring (str "Returns a vector of all matching elements scoped to `element` for a query.\n\n " + "Returns an empty vector if no elements match.\n\n " + "- No match: Returns []\n " + "- One match: Returns vector\n " + "- Multiple match: Returns vector\n " + "- Async: No") + :template query-all-fn-template} + {:type "findAllBy" + :docstring (str "Returns a promise which resolves to a vector of matching elements scoped to `element`.\n\n " + "The promise is rejected if no elements are found after a default timeout of 1000ms.\n " + "This is a combination of `get-all-by` queries and `waitFor`.\n\n " + "- No match: Rejects\n " + "- One match: Resolves with vector\n " + "- Multiple match: Resolves with vector\n " + "- Async: Yes") + :template query-all-fn-template}] + query-values [{:by "Role" + :url "https://testing-library.com/docs/queries/byrole"} + {:by "LabelText" + :url "https://testing-library.com/docs/queries/bylabeltext"} + {:by "PlaceholderText" + :url "https://testing-library.com/docs/queries/byplaceholdertext"} + {:by "Text" + :url "https://testing-library.com/docs/queries/bytext"} + {:by "DisplayValue" + :url "https://testing-library.com/docs/queries/bydisplayvalue"} + {:by "AltText" + :url "https://testing-library.com/docs/queries/byalttext"} + {:by "Title" + :url "https://testing-library.com/docs/queries/bytitle"} + {:by "TestId" + :url "https://testing-library.com/docs/queries/bytestid"}] + queries (for [query-type query-types + query-value query-values] + (let [js-fn-name (str (:type query-type) (:by query-value)) + cljs-fn-name (camel-case->kebab-case js-fn-name)] + {:cljs-fn-name cljs-fn-name + :js-fn-name js-fn-name + :url (:url query-value) + :docstring (:docstring query-type) + :template (:template query-type)})) + query-fns (->> queries + (map #(-> (:template %) + (str/replace "$cljs-fn-name" (:cljs-fn-name %)) + (str/replace "$js-fn-name" (:js-fn-name %)) + (str/replace "$docstring" (:docstring %)) + (str/replace "$url" (:url %)))) + (str/join "\n")) + source-file (slurp within-source-file) + begin-position (+ (str/index-of source-file begin-marker) (count begin-marker)) + end-position (str/index-of source-file end-marker) + source-file-updated (-> source-file + (remove-string-between begin-position end-position) + (insert-string query-fns begin-position))] + (spit within-source-file source-file-updated)) diff --git a/scripts/within_query_all_fn.template.clj b/scripts/within_query_all_fn.template.clj new file mode 100644 index 0000000..9061a90 --- /dev/null +++ b/scripts/within_query_all_fn.template.clj @@ -0,0 +1,8 @@ +(defn $cljs-fn-name + "$docstring + + See [$js-fn-name]($url)." + ([element matcher] + (vec (.$js-fn-name (within element) matcher))) + ([element matcher options] + (vec (.$js-fn-name (within element) matcher (clj->js options))))) diff --git a/scripts/within_query_fn.template.clj b/scripts/within_query_fn.template.clj new file mode 100644 index 0000000..b1d3b6f --- /dev/null +++ b/scripts/within_query_fn.template.clj @@ -0,0 +1,8 @@ +(defn $cljs-fn-name + "$docstring + + See [$js-fn-name]($url)." + ([element matcher] + (.$js-fn-name (within element) matcher)) + ([element matcher options] + (.$js-fn-name (within element) matcher (clj->js options)))) From 3b937d9e71ccb24949428e1b562aa9a19bdca7db Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Wed, 25 Feb 2026 10:25:35 +0200 Subject: [PATCH 2/5] Generate within namespace --- .../react_testing_library_cljs/within.cljs | 814 ++++++++++++++++++ 1 file changed, 814 insertions(+) create mode 100644 src/main/react_testing_library_cljs/within.cljs diff --git a/src/main/react_testing_library_cljs/within.cljs b/src/main/react_testing_library_cljs/within.cljs new file mode 100644 index 0000000..625810c --- /dev/null +++ b/src/main/react_testing_library_cljs/within.cljs @@ -0,0 +1,814 @@ +(ns react-testing-library-cljs.within + (:require + ["@testing-library/react" :refer [within]])) + +;; Refer to https://testing-library.com/docs/queries/about/ for more details about queries +;; Begin - Generated Code (Do not modify manually) +(defn get-by-role + "Returns the matching element scoped to `element` for a query. + + Throws a descriptive error if no elements match or if more than one match is found. + Use `get-all-by` instead if more than one element is expected. + + - No match: Throws error + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [getByRole](https://testing-library.com/docs/queries/byrole)." + ([element matcher] + (.getByRole (within element) matcher)) + ([element matcher options] + (.getByRole (within element) matcher (clj->js options)))) + +(defn get-by-label-text + "Returns the matching element scoped to `element` for a query. + + Throws a descriptive error if no elements match or if more than one match is found. + Use `get-all-by` instead if more than one element is expected. + + - No match: Throws error + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [getByLabelText](https://testing-library.com/docs/queries/bylabeltext)." + ([element matcher] + (.getByLabelText (within element) matcher)) + ([element matcher options] + (.getByLabelText (within element) matcher (clj->js options)))) + +(defn get-by-placeholder-text + "Returns the matching element scoped to `element` for a query. + + Throws a descriptive error if no elements match or if more than one match is found. + Use `get-all-by` instead if more than one element is expected. + + - No match: Throws error + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [getByPlaceholderText](https://testing-library.com/docs/queries/byplaceholdertext)." + ([element matcher] + (.getByPlaceholderText (within element) matcher)) + ([element matcher options] + (.getByPlaceholderText (within element) matcher (clj->js options)))) + +(defn get-by-text + "Returns the matching element scoped to `element` for a query. + + Throws a descriptive error if no elements match or if more than one match is found. + Use `get-all-by` instead if more than one element is expected. + + - No match: Throws error + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [getByText](https://testing-library.com/docs/queries/bytext)." + ([element matcher] + (.getByText (within element) matcher)) + ([element matcher options] + (.getByText (within element) matcher (clj->js options)))) + +(defn get-by-display-value + "Returns the matching element scoped to `element` for a query. + + Throws a descriptive error if no elements match or if more than one match is found. + Use `get-all-by` instead if more than one element is expected. + + - No match: Throws error + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [getByDisplayValue](https://testing-library.com/docs/queries/bydisplayvalue)." + ([element matcher] + (.getByDisplayValue (within element) matcher)) + ([element matcher options] + (.getByDisplayValue (within element) matcher (clj->js options)))) + +(defn get-by-alt-text + "Returns the matching element scoped to `element` for a query. + + Throws a descriptive error if no elements match or if more than one match is found. + Use `get-all-by` instead if more than one element is expected. + + - No match: Throws error + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [getByAltText](https://testing-library.com/docs/queries/byalttext)." + ([element matcher] + (.getByAltText (within element) matcher)) + ([element matcher options] + (.getByAltText (within element) matcher (clj->js options)))) + +(defn get-by-title + "Returns the matching element scoped to `element` for a query. + + Throws a descriptive error if no elements match or if more than one match is found. + Use `get-all-by` instead if more than one element is expected. + + - No match: Throws error + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [getByTitle](https://testing-library.com/docs/queries/bytitle)." + ([element matcher] + (.getByTitle (within element) matcher)) + ([element matcher options] + (.getByTitle (within element) matcher (clj->js options)))) + +(defn get-by-test-id + "Returns the matching element scoped to `element` for a query. + + Throws a descriptive error if no elements match or if more than one match is found. + Use `get-all-by` instead if more than one element is expected. + + - No match: Throws error + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [getByTestId](https://testing-library.com/docs/queries/bytestid)." + ([element matcher] + (.getByTestId (within element) matcher)) + ([element matcher options] + (.getByTestId (within element) matcher (clj->js options)))) + +(defn query-by-role + "Returns the matching element scoped to `element` for a query, or `nil` if no elements match. + + Useful for asserting an element that is not present. Throws an error if more than one + match is found. Use `query-all-by` instead if this is OK. + + - No match: Returns nil + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [queryByRole](https://testing-library.com/docs/queries/byrole)." + ([element matcher] + (.queryByRole (within element) matcher)) + ([element matcher options] + (.queryByRole (within element) matcher (clj->js options)))) + +(defn query-by-label-text + "Returns the matching element scoped to `element` for a query, or `nil` if no elements match. + + Useful for asserting an element that is not present. Throws an error if more than one + match is found. Use `query-all-by` instead if this is OK. + + - No match: Returns nil + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [queryByLabelText](https://testing-library.com/docs/queries/bylabeltext)." + ([element matcher] + (.queryByLabelText (within element) matcher)) + ([element matcher options] + (.queryByLabelText (within element) matcher (clj->js options)))) + +(defn query-by-placeholder-text + "Returns the matching element scoped to `element` for a query, or `nil` if no elements match. + + Useful for asserting an element that is not present. Throws an error if more than one + match is found. Use `query-all-by` instead if this is OK. + + - No match: Returns nil + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [queryByPlaceholderText](https://testing-library.com/docs/queries/byplaceholdertext)." + ([element matcher] + (.queryByPlaceholderText (within element) matcher)) + ([element matcher options] + (.queryByPlaceholderText (within element) matcher (clj->js options)))) + +(defn query-by-text + "Returns the matching element scoped to `element` for a query, or `nil` if no elements match. + + Useful for asserting an element that is not present. Throws an error if more than one + match is found. Use `query-all-by` instead if this is OK. + + - No match: Returns nil + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [queryByText](https://testing-library.com/docs/queries/bytext)." + ([element matcher] + (.queryByText (within element) matcher)) + ([element matcher options] + (.queryByText (within element) matcher (clj->js options)))) + +(defn query-by-display-value + "Returns the matching element scoped to `element` for a query, or `nil` if no elements match. + + Useful for asserting an element that is not present. Throws an error if more than one + match is found. Use `query-all-by` instead if this is OK. + + - No match: Returns nil + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [queryByDisplayValue](https://testing-library.com/docs/queries/bydisplayvalue)." + ([element matcher] + (.queryByDisplayValue (within element) matcher)) + ([element matcher options] + (.queryByDisplayValue (within element) matcher (clj->js options)))) + +(defn query-by-alt-text + "Returns the matching element scoped to `element` for a query, or `nil` if no elements match. + + Useful for asserting an element that is not present. Throws an error if more than one + match is found. Use `query-all-by` instead if this is OK. + + - No match: Returns nil + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [queryByAltText](https://testing-library.com/docs/queries/byalttext)." + ([element matcher] + (.queryByAltText (within element) matcher)) + ([element matcher options] + (.queryByAltText (within element) matcher (clj->js options)))) + +(defn query-by-title + "Returns the matching element scoped to `element` for a query, or `nil` if no elements match. + + Useful for asserting an element that is not present. Throws an error if more than one + match is found. Use `query-all-by` instead if this is OK. + + - No match: Returns nil + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [queryByTitle](https://testing-library.com/docs/queries/bytitle)." + ([element matcher] + (.queryByTitle (within element) matcher)) + ([element matcher options] + (.queryByTitle (within element) matcher (clj->js options)))) + +(defn query-by-test-id + "Returns the matching element scoped to `element` for a query, or `nil` if no elements match. + + Useful for asserting an element that is not present. Throws an error if more than one + match is found. Use `query-all-by` instead if this is OK. + + - No match: Returns nil + - One match: Returns element + - Multiple match: Throws error + - Async: No + + See [queryByTestId](https://testing-library.com/docs/queries/bytestid)." + ([element matcher] + (.queryByTestId (within element) matcher)) + ([element matcher options] + (.queryByTestId (within element) matcher (clj->js options)))) + +(defn find-by-role + "Returns a promise which resolves when a matching element is found within `element`. + + The promise is rejected if no element is found or if more than one element is found + after a default timeout of 1000ms. If you need to find more than one element, use + `find-all-by`. This is a combination of `get-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with element + - Multiple match: Rejects + - Async: Yes + + See [findByRole](https://testing-library.com/docs/queries/byrole)." + ([element matcher] + (.findByRole (within element) matcher)) + ([element matcher options] + (.findByRole (within element) matcher (clj->js options)))) + +(defn find-by-label-text + "Returns a promise which resolves when a matching element is found within `element`. + + The promise is rejected if no element is found or if more than one element is found + after a default timeout of 1000ms. If you need to find more than one element, use + `find-all-by`. This is a combination of `get-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with element + - Multiple match: Rejects + - Async: Yes + + See [findByLabelText](https://testing-library.com/docs/queries/bylabeltext)." + ([element matcher] + (.findByLabelText (within element) matcher)) + ([element matcher options] + (.findByLabelText (within element) matcher (clj->js options)))) + +(defn find-by-placeholder-text + "Returns a promise which resolves when a matching element is found within `element`. + + The promise is rejected if no element is found or if more than one element is found + after a default timeout of 1000ms. If you need to find more than one element, use + `find-all-by`. This is a combination of `get-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with element + - Multiple match: Rejects + - Async: Yes + + See [findByPlaceholderText](https://testing-library.com/docs/queries/byplaceholdertext)." + ([element matcher] + (.findByPlaceholderText (within element) matcher)) + ([element matcher options] + (.findByPlaceholderText (within element) matcher (clj->js options)))) + +(defn find-by-text + "Returns a promise which resolves when a matching element is found within `element`. + + The promise is rejected if no element is found or if more than one element is found + after a default timeout of 1000ms. If you need to find more than one element, use + `find-all-by`. This is a combination of `get-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with element + - Multiple match: Rejects + - Async: Yes + + See [findByText](https://testing-library.com/docs/queries/bytext)." + ([element matcher] + (.findByText (within element) matcher)) + ([element matcher options] + (.findByText (within element) matcher (clj->js options)))) + +(defn find-by-display-value + "Returns a promise which resolves when a matching element is found within `element`. + + The promise is rejected if no element is found or if more than one element is found + after a default timeout of 1000ms. If you need to find more than one element, use + `find-all-by`. This is a combination of `get-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with element + - Multiple match: Rejects + - Async: Yes + + See [findByDisplayValue](https://testing-library.com/docs/queries/bydisplayvalue)." + ([element matcher] + (.findByDisplayValue (within element) matcher)) + ([element matcher options] + (.findByDisplayValue (within element) matcher (clj->js options)))) + +(defn find-by-alt-text + "Returns a promise which resolves when a matching element is found within `element`. + + The promise is rejected if no element is found or if more than one element is found + after a default timeout of 1000ms. If you need to find more than one element, use + `find-all-by`. This is a combination of `get-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with element + - Multiple match: Rejects + - Async: Yes + + See [findByAltText](https://testing-library.com/docs/queries/byalttext)." + ([element matcher] + (.findByAltText (within element) matcher)) + ([element matcher options] + (.findByAltText (within element) matcher (clj->js options)))) + +(defn find-by-title + "Returns a promise which resolves when a matching element is found within `element`. + + The promise is rejected if no element is found or if more than one element is found + after a default timeout of 1000ms. If you need to find more than one element, use + `find-all-by`. This is a combination of `get-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with element + - Multiple match: Rejects + - Async: Yes + + See [findByTitle](https://testing-library.com/docs/queries/bytitle)." + ([element matcher] + (.findByTitle (within element) matcher)) + ([element matcher options] + (.findByTitle (within element) matcher (clj->js options)))) + +(defn find-by-test-id + "Returns a promise which resolves when a matching element is found within `element`. + + The promise is rejected if no element is found or if more than one element is found + after a default timeout of 1000ms. If you need to find more than one element, use + `find-all-by`. This is a combination of `get-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with element + - Multiple match: Rejects + - Async: Yes + + See [findByTestId](https://testing-library.com/docs/queries/bytestid)." + ([element matcher] + (.findByTestId (within element) matcher)) + ([element matcher options] + (.findByTestId (within element) matcher (clj->js options)))) + +(defn get-all-by-role + "Returns a vector of all matching elements scoped to `element` for a query. + + Throws an error if no elements match. + + - No match: Throws error + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [getAllByRole](https://testing-library.com/docs/queries/byrole)." + ([element matcher] + (vec (.getAllByRole (within element) matcher))) + ([element matcher options] + (vec (.getAllByRole (within element) matcher (clj->js options))))) + +(defn get-all-by-label-text + "Returns a vector of all matching elements scoped to `element` for a query. + + Throws an error if no elements match. + + - No match: Throws error + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [getAllByLabelText](https://testing-library.com/docs/queries/bylabeltext)." + ([element matcher] + (vec (.getAllByLabelText (within element) matcher))) + ([element matcher options] + (vec (.getAllByLabelText (within element) matcher (clj->js options))))) + +(defn get-all-by-placeholder-text + "Returns a vector of all matching elements scoped to `element` for a query. + + Throws an error if no elements match. + + - No match: Throws error + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [getAllByPlaceholderText](https://testing-library.com/docs/queries/byplaceholdertext)." + ([element matcher] + (vec (.getAllByPlaceholderText (within element) matcher))) + ([element matcher options] + (vec (.getAllByPlaceholderText (within element) matcher (clj->js options))))) + +(defn get-all-by-text + "Returns a vector of all matching elements scoped to `element` for a query. + + Throws an error if no elements match. + + - No match: Throws error + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [getAllByText](https://testing-library.com/docs/queries/bytext)." + ([element matcher] + (vec (.getAllByText (within element) matcher))) + ([element matcher options] + (vec (.getAllByText (within element) matcher (clj->js options))))) + +(defn get-all-by-display-value + "Returns a vector of all matching elements scoped to `element` for a query. + + Throws an error if no elements match. + + - No match: Throws error + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [getAllByDisplayValue](https://testing-library.com/docs/queries/bydisplayvalue)." + ([element matcher] + (vec (.getAllByDisplayValue (within element) matcher))) + ([element matcher options] + (vec (.getAllByDisplayValue (within element) matcher (clj->js options))))) + +(defn get-all-by-alt-text + "Returns a vector of all matching elements scoped to `element` for a query. + + Throws an error if no elements match. + + - No match: Throws error + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [getAllByAltText](https://testing-library.com/docs/queries/byalttext)." + ([element matcher] + (vec (.getAllByAltText (within element) matcher))) + ([element matcher options] + (vec (.getAllByAltText (within element) matcher (clj->js options))))) + +(defn get-all-by-title + "Returns a vector of all matching elements scoped to `element` for a query. + + Throws an error if no elements match. + + - No match: Throws error + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [getAllByTitle](https://testing-library.com/docs/queries/bytitle)." + ([element matcher] + (vec (.getAllByTitle (within element) matcher))) + ([element matcher options] + (vec (.getAllByTitle (within element) matcher (clj->js options))))) + +(defn get-all-by-test-id + "Returns a vector of all matching elements scoped to `element` for a query. + + Throws an error if no elements match. + + - No match: Throws error + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [getAllByTestId](https://testing-library.com/docs/queries/bytestid)." + ([element matcher] + (vec (.getAllByTestId (within element) matcher))) + ([element matcher options] + (vec (.getAllByTestId (within element) matcher (clj->js options))))) + +(defn query-all-by-role + "Returns a vector of all matching elements scoped to `element` for a query. + + Returns an empty vector if no elements match. + + - No match: Returns [] + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [queryAllByRole](https://testing-library.com/docs/queries/byrole)." + ([element matcher] + (vec (.queryAllByRole (within element) matcher))) + ([element matcher options] + (vec (.queryAllByRole (within element) matcher (clj->js options))))) + +(defn query-all-by-label-text + "Returns a vector of all matching elements scoped to `element` for a query. + + Returns an empty vector if no elements match. + + - No match: Returns [] + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [queryAllByLabelText](https://testing-library.com/docs/queries/bylabeltext)." + ([element matcher] + (vec (.queryAllByLabelText (within element) matcher))) + ([element matcher options] + (vec (.queryAllByLabelText (within element) matcher (clj->js options))))) + +(defn query-all-by-placeholder-text + "Returns a vector of all matching elements scoped to `element` for a query. + + Returns an empty vector if no elements match. + + - No match: Returns [] + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [queryAllByPlaceholderText](https://testing-library.com/docs/queries/byplaceholdertext)." + ([element matcher] + (vec (.queryAllByPlaceholderText (within element) matcher))) + ([element matcher options] + (vec (.queryAllByPlaceholderText (within element) matcher (clj->js options))))) + +(defn query-all-by-text + "Returns a vector of all matching elements scoped to `element` for a query. + + Returns an empty vector if no elements match. + + - No match: Returns [] + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [queryAllByText](https://testing-library.com/docs/queries/bytext)." + ([element matcher] + (vec (.queryAllByText (within element) matcher))) + ([element matcher options] + (vec (.queryAllByText (within element) matcher (clj->js options))))) + +(defn query-all-by-display-value + "Returns a vector of all matching elements scoped to `element` for a query. + + Returns an empty vector if no elements match. + + - No match: Returns [] + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [queryAllByDisplayValue](https://testing-library.com/docs/queries/bydisplayvalue)." + ([element matcher] + (vec (.queryAllByDisplayValue (within element) matcher))) + ([element matcher options] + (vec (.queryAllByDisplayValue (within element) matcher (clj->js options))))) + +(defn query-all-by-alt-text + "Returns a vector of all matching elements scoped to `element` for a query. + + Returns an empty vector if no elements match. + + - No match: Returns [] + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [queryAllByAltText](https://testing-library.com/docs/queries/byalttext)." + ([element matcher] + (vec (.queryAllByAltText (within element) matcher))) + ([element matcher options] + (vec (.queryAllByAltText (within element) matcher (clj->js options))))) + +(defn query-all-by-title + "Returns a vector of all matching elements scoped to `element` for a query. + + Returns an empty vector if no elements match. + + - No match: Returns [] + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [queryAllByTitle](https://testing-library.com/docs/queries/bytitle)." + ([element matcher] + (vec (.queryAllByTitle (within element) matcher))) + ([element matcher options] + (vec (.queryAllByTitle (within element) matcher (clj->js options))))) + +(defn query-all-by-test-id + "Returns a vector of all matching elements scoped to `element` for a query. + + Returns an empty vector if no elements match. + + - No match: Returns [] + - One match: Returns vector + - Multiple match: Returns vector + - Async: No + + See [queryAllByTestId](https://testing-library.com/docs/queries/bytestid)." + ([element matcher] + (vec (.queryAllByTestId (within element) matcher))) + ([element matcher options] + (vec (.queryAllByTestId (within element) matcher (clj->js options))))) + +(defn find-all-by-role + "Returns a promise which resolves to a vector of matching elements scoped to `element`. + + The promise is rejected if no elements are found after a default timeout of 1000ms. + This is a combination of `get-all-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with vector + - Multiple match: Resolves with vector + - Async: Yes + + See [findAllByRole](https://testing-library.com/docs/queries/byrole)." + ([element matcher] + (vec (.findAllByRole (within element) matcher))) + ([element matcher options] + (vec (.findAllByRole (within element) matcher (clj->js options))))) + +(defn find-all-by-label-text + "Returns a promise which resolves to a vector of matching elements scoped to `element`. + + The promise is rejected if no elements are found after a default timeout of 1000ms. + This is a combination of `get-all-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with vector + - Multiple match: Resolves with vector + - Async: Yes + + See [findAllByLabelText](https://testing-library.com/docs/queries/bylabeltext)." + ([element matcher] + (vec (.findAllByLabelText (within element) matcher))) + ([element matcher options] + (vec (.findAllByLabelText (within element) matcher (clj->js options))))) + +(defn find-all-by-placeholder-text + "Returns a promise which resolves to a vector of matching elements scoped to `element`. + + The promise is rejected if no elements are found after a default timeout of 1000ms. + This is a combination of `get-all-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with vector + - Multiple match: Resolves with vector + - Async: Yes + + See [findAllByPlaceholderText](https://testing-library.com/docs/queries/byplaceholdertext)." + ([element matcher] + (vec (.findAllByPlaceholderText (within element) matcher))) + ([element matcher options] + (vec (.findAllByPlaceholderText (within element) matcher (clj->js options))))) + +(defn find-all-by-text + "Returns a promise which resolves to a vector of matching elements scoped to `element`. + + The promise is rejected if no elements are found after a default timeout of 1000ms. + This is a combination of `get-all-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with vector + - Multiple match: Resolves with vector + - Async: Yes + + See [findAllByText](https://testing-library.com/docs/queries/bytext)." + ([element matcher] + (vec (.findAllByText (within element) matcher))) + ([element matcher options] + (vec (.findAllByText (within element) matcher (clj->js options))))) + +(defn find-all-by-display-value + "Returns a promise which resolves to a vector of matching elements scoped to `element`. + + The promise is rejected if no elements are found after a default timeout of 1000ms. + This is a combination of `get-all-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with vector + - Multiple match: Resolves with vector + - Async: Yes + + See [findAllByDisplayValue](https://testing-library.com/docs/queries/bydisplayvalue)." + ([element matcher] + (vec (.findAllByDisplayValue (within element) matcher))) + ([element matcher options] + (vec (.findAllByDisplayValue (within element) matcher (clj->js options))))) + +(defn find-all-by-alt-text + "Returns a promise which resolves to a vector of matching elements scoped to `element`. + + The promise is rejected if no elements are found after a default timeout of 1000ms. + This is a combination of `get-all-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with vector + - Multiple match: Resolves with vector + - Async: Yes + + See [findAllByAltText](https://testing-library.com/docs/queries/byalttext)." + ([element matcher] + (vec (.findAllByAltText (within element) matcher))) + ([element matcher options] + (vec (.findAllByAltText (within element) matcher (clj->js options))))) + +(defn find-all-by-title + "Returns a promise which resolves to a vector of matching elements scoped to `element`. + + The promise is rejected if no elements are found after a default timeout of 1000ms. + This is a combination of `get-all-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with vector + - Multiple match: Resolves with vector + - Async: Yes + + See [findAllByTitle](https://testing-library.com/docs/queries/bytitle)." + ([element matcher] + (vec (.findAllByTitle (within element) matcher))) + ([element matcher options] + (vec (.findAllByTitle (within element) matcher (clj->js options))))) + +(defn find-all-by-test-id + "Returns a promise which resolves to a vector of matching elements scoped to `element`. + + The promise is rejected if no elements are found after a default timeout of 1000ms. + This is a combination of `get-all-by` queries and `waitFor`. + + - No match: Rejects + - One match: Resolves with vector + - Multiple match: Resolves with vector + - Async: Yes + + See [findAllByTestId](https://testing-library.com/docs/queries/bytestid)." + ([element matcher] + (vec (.findAllByTestId (within element) matcher))) + ([element matcher options] + (vec (.findAllByTestId (within element) matcher (clj->js options))))) +;; End - Generated Code (Do not modify manually) From 1404d4889813efec9129b486d3bda617647f74e3 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Wed, 25 Feb 2026 10:25:51 +0200 Subject: [PATCH 3/5] Add simple unit test for within --- .../within_test.cljs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/test/react_testing_library_cljs/within_test.cljs diff --git a/src/test/react_testing_library_cljs/within_test.cljs b/src/test/react_testing_library_cljs/within_test.cljs new file mode 100644 index 0000000..e3a6d49 --- /dev/null +++ b/src/test/react_testing_library_cljs/within_test.cljs @@ -0,0 +1,110 @@ +(ns react-testing-library-cljs.within-test + (:require + [cljs.test :refer [deftest is testing]] + ["@testing-library/react" :as rtl] + ["react" :as react] + [react-testing-library-cljs.screen :as screen] + [react-testing-library-cljs.within :as within])) + +(defn- render-el [element] + (rtl/render element)) + +(deftest get-by-role-test + (testing "returns element within scope" + (render-el (react/createElement "div" #js {:role "region"} + (react/createElement "button" nil "Inside"))) + (let [region (screen/get-by-role "region") + btn (within/get-by-role region "button")] + (is (some? btn)) + (is (= "Inside" (.-textContent btn))))) + + (testing "scopes query — does not find element outside" + (rtl/cleanup) + (render-el (react/createElement "div" nil + (react/createElement "div" #js {:role "region"} + (react/createElement "button" nil "Inside")) + (react/createElement "button" nil "Outside"))) + (let [region (screen/get-by-role "region")] + (is (thrown? js/Error (within/get-by-role region "button" {:name "Outside"}))))) + + (testing "supports options map" + (rtl/cleanup) + (render-el (react/createElement "div" #js {:role "region"} + (react/createElement "button" nil "Submit"))) + (let [region (screen/get-by-role "region") + btn (within/get-by-role region "button" {:name "Submit"})] + (is (some? btn))))) + +(deftest query-by-role-test + (testing "returns nil when element not found within scope" + (rtl/cleanup) + (render-el (react/createElement "div" nil + (react/createElement "div" #js {:role "region"} "no button here") + (react/createElement "button" nil "Outside"))) + (let [region (screen/get-by-role "region")] + (is (nil? (within/query-by-role region "button")))))) + +(deftest get-all-by-role-test + (testing "returns vector of elements within scope" + (rtl/cleanup) + (render-el (react/createElement "div" #js {:role "region"} + (react/createElement "button" nil "One") + (react/createElement "button" nil "Two"))) + (let [region (screen/get-by-role "region") + btns (within/get-all-by-role region "button")] + (is (vector? btns)) + (is (= 2 (count btns))))) + + (testing "does not include elements outside scope" + (rtl/cleanup) + (render-el (react/createElement "div" nil + (react/createElement "div" #js {:role "region"} + (react/createElement "button" nil "One")) + (react/createElement "button" nil "Outside"))) + (let [region (screen/get-by-role "region") + btns (within/get-all-by-role region "button")] + (is (= 1 (count btns)))))) + +(deftest query-all-by-role-test + (testing "returns empty vector when no elements found within scope" + (rtl/cleanup) + (render-el (react/createElement "div" #js {:role "region"} "no buttons")) + (let [region (screen/get-by-role "region")] + (is (= [] (within/query-all-by-role region "button")))))) + +(deftest get-by-text-test + (testing "returns element with matching text within scope" + (rtl/cleanup) + (render-el (react/createElement "div" #js {:role "region"} + (react/createElement "span" nil "Hello"))) + (let [region (screen/get-by-role "region") + el (within/get-by-text region "Hello")] + (is (some? el)) + (is (= "Hello" (.-textContent el))))) + + (testing "scopes query — does not find text outside" + (rtl/cleanup) + (render-el (react/createElement "div" nil + (react/createElement "div" #js {:role "region"} + (react/createElement "span" nil "Inside")) + (react/createElement "span" nil "Outside"))) + (let [region (screen/get-by-role "region")] + (is (nil? (within/query-by-text region "Outside")))))) + +(deftest get-by-test-id-test + (testing "returns element by test id within scope" + (rtl/cleanup) + (render-el (react/createElement "div" #js {:role "region"} + (react/createElement "span" #js {:data-testid "my-id"} "content"))) + (let [region (screen/get-by-role "region") + el (within/get-by-test-id region "my-id")] + (is (some? el))))) + +(deftest get-by-placeholder-text-test + (testing "returns input by placeholder within scope" + (rtl/cleanup) + (render-el (react/createElement "form" #js {:aria-label "search form"} + (react/createElement "input" #js {:placeholder "Enter name"}))) + (let [form (screen/get-by-role "form") + el (within/get-by-placeholder-text form "Enter name")] + (is (some? el))))) From 1908da3a2e14dff09ad668cfedc0a64259bb96f7 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Wed, 25 Feb 2026 20:31:22 +0300 Subject: [PATCH 4/5] Update bb tasks --- bb.edn | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bb.edn b/bb.edn index b995a19..f1ef440 100644 --- a/bb.edn +++ b/bb.edn @@ -2,6 +2,7 @@ :tasks {generate:screen {:task (shell {:dir "scripts"} "bb" "generate_screen.clj")} generate:fire-event {:task (shell {:dir "scripts"} "bb" "generate_fire_event.clj")} - generate {:depends [generate:screen generate:fire-event]} + generate:within {:task (shell {:dir "scripts"} "bb" "generate_within.clj")} + generate {:depends [generate:screen generate:fire-event generate:within]} release {:doc "Perform release" :task (shell "lein release")}}} From 6ebe770e69627fde017aec70bec779ba8d829770 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Wed, 25 Feb 2026 20:38:02 +0300 Subject: [PATCH 5/5] DRY Create common.clj with shared fns --- scripts/common.clj | 19 ++++++++++++++++++ scripts/generate_fire_event.clj | 20 ++----------------- scripts/generate_screen.clj | 20 ++----------------- scripts/generate_within.clj | 20 ++----------------- .../react_testing_library_cljs/screen.cljs | 1 - 5 files changed, 25 insertions(+), 55 deletions(-) create mode 100644 scripts/common.clj diff --git a/scripts/common.clj b/scripts/common.clj new file mode 100644 index 0000000..f24db23 --- /dev/null +++ b/scripts/common.clj @@ -0,0 +1,19 @@ +(require '[clojure.string :as str]) + +(defn insert-string [original string position] + (str (subs original 0 position) + string + (subs original position))) + +(defn remove-string-between [string begin end] + (str (subs string 0 begin) + (subs string end))) + +(defn camel-case->kebab-case [string] + (reduce + (fn [result char] + (if (= (str char) (str/upper-case char)) + (str result "-" (str/lower-case char)) + (str result char))) + "" + string)) diff --git a/scripts/generate_fire_event.clj b/scripts/generate_fire_event.clj index ca35360..0c96a1d 100644 --- a/scripts/generate_fire_event.clj +++ b/scripts/generate_fire_event.clj @@ -2,6 +2,8 @@ (require '[clojure.string :as str]) +(load-file "common.clj") + (def event-types ["copy" "cut" @@ -90,24 +92,6 @@ (def begin-marker ";; Begin - Generated Code (Do not modify manually)\n") (def end-marker ";; End - Generated Code (Do not modify manually)\n") -(defn insert-string [original string position] - (str (subs original 0 position) - string - (subs original position))) - -(defn remove-string-between [string begin end] - (str (subs string 0 begin) - (subs string end))) - -(defn camel-case->kebab-case [string] - (reduce - (fn [result char] - (if (= (str char) (str/upper-case char)) - (str result "-" (str/lower-case char)) - (str result char))) - "" - string)) - (let [fire-event-source-file "../src/main/react_testing_library_cljs/fire_event.cljs" fire-event-fn-template (slurp "fire_event_fn.template.clj") event-type-fns (->> event-types diff --git a/scripts/generate_screen.clj b/scripts/generate_screen.clj index dd38f87..5ccbfcb 100644 --- a/scripts/generate_screen.clj +++ b/scripts/generate_screen.clj @@ -2,6 +2,8 @@ (require '[clojure.string :as str]) +(load-file "common.clj") + (def screen-source-file "../src/main/react_testing_library_cljs/screen.cljs") (def begin-marker ";; Begin - Generated Code (Do not modify manually)\n") @@ -10,24 +12,6 @@ (def query-fn-template (slurp "query_fn.template.clj")) (def query-all-fn-template (slurp "query_all_fn.template.clj")) -(defn insert-string [original string position] - (str (subs original 0 position) - string - (subs original position))) - -(defn remove-string-between [string begin end] - (str (subs string 0 begin) - (subs string end))) - -(defn camel-case->kebab-case [string] - (reduce - (fn [result char] - (if (= (str char) (str/upper-case char)) - (str result "-" (str/lower-case char)) - (str result char))) - "" - string)) - (let [query-types [{:type "getBy" :docstring (str "Returns the matching element for a query.\n\n " "Throws a descriptive error if no elements match or if more than one match is found.\n " diff --git a/scripts/generate_within.clj b/scripts/generate_within.clj index 0d87a64..43af21a 100644 --- a/scripts/generate_within.clj +++ b/scripts/generate_within.clj @@ -2,6 +2,8 @@ (require '[clojure.string :as str]) +(load-file "common.clj") + (def within-source-file "../src/main/react_testing_library_cljs/within.cljs") (def begin-marker ";; Begin - Generated Code (Do not modify manually)\n") @@ -10,24 +12,6 @@ (def query-fn-template (slurp "within_query_fn.template.clj")) (def query-all-fn-template (slurp "within_query_all_fn.template.clj")) -(defn insert-string [original string position] - (str (subs original 0 position) - string - (subs original position))) - -(defn remove-string-between [string begin end] - (str (subs string 0 begin) - (subs string end))) - -(defn camel-case->kebab-case [string] - (reduce - (fn [result char] - (if (= (str char) (str/upper-case char)) - (str result "-" (str/lower-case char)) - (str result char))) - "" - string)) - (let [query-types [{:type "getBy" :docstring (str "Returns the matching element scoped to `element` for a query.\n\n " "Throws a descriptive error if no elements match or if more than one match is found.\n " diff --git a/src/main/react_testing_library_cljs/screen.cljs b/src/main/react_testing_library_cljs/screen.cljs index 63be3db..5429dd0 100644 --- a/src/main/react_testing_library_cljs/screen.cljs +++ b/src/main/react_testing_library_cljs/screen.cljs @@ -815,5 +815,4 @@ (vec (.findAllByTestId screen matcher))) ([matcher options] (vec (.findAllByTestId screen matcher (clj->js options))))) - ;; End - Generated Code (Do not modify manually)