diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d051e626886..010a6781aef 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -123,3 +123,21 @@ jobs: run: | python -m build . twine check dist/* + + prettier: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + - run: > + npx prettier@3.5 + --check + "sphinx/themes/**/*.js" + "!sphinx/themes/bizstyle/static/css3-mediaqueries*.js" + "tests/js/**/*.{js,mjs}" + "!tests/js/fixtures/**" diff --git a/.prettierrc.toml b/.prettierrc.toml new file mode 100644 index 00000000000..1799612bfdd --- /dev/null +++ b/.prettierrc.toml @@ -0,0 +1,2 @@ +# https://prettier.io/docs/options +experimentalOperatorPosition = "start" diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index cc997bf6456..187e6a2f37f 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -81,11 +81,11 @@ class SearchLanguage: /** * Dummy stemmer for languages without stemming rules. */ -var Stemmer = function() { - this.stemWord = function(w) { +var Stemmer = function () { + this.stemWord = function (w) { return w; - } -} + }; +}; """ _word_re = re.compile(r'\w+') diff --git a/sphinx/themes/basic/static/doctools.js b/sphinx/themes/basic/static/doctools.js index 0398ebb9f03..807cdb176c0 100644 --- a/sphinx/themes/basic/static/doctools.js +++ b/sphinx/themes/basic/static/doctools.js @@ -59,7 +59,7 @@ const Documentation = { Object.assign(Documentation.TRANSLATIONS, catalog.messages); Documentation.PLURAL_EXPR = new Function( "n", - `return (${catalog.plural_expr})` + `return (${catalog.plural_expr})`, ); Documentation.LOCALE = catalog.locale; }, @@ -89,7 +89,7 @@ const Documentation = { const togglerElements = document.querySelectorAll("img.toggler"); togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)) + el.addEventListener("click", (event) => toggler(event.currentTarget)), ); togglerElements.forEach((el) => (el.style.display = "")); if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); @@ -98,14 +98,15 @@ const Documentation = { initOnKeyListeners: () => { // only install a listener if it is really needed if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && - !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS + && !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS ) return; document.addEventListener("keydown", (event) => { // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) + return; // bail with special keys if (event.altKey || event.ctrlKey || event.metaKey) return; diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index ba5e67aa75e..5a7628a18a2 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -41,11 +41,12 @@ if (typeof Scorer === "undefined") { } // Global search result kind enum, used by themes to style search results. +// prettier-ignore class SearchResultKind { - static get index() { return "index"; } - static get object() { return "object"; } - static get text() { return "text"; } - static get title() { return "title"; } + static get index() { return "index"; } + static get object() { return "object"; } + static get text() { return "text"; } + static get title() { return "title"; } } const _removeChildren = (element) => { @@ -95,20 +96,25 @@ const _displayItem = (item, searchTerms, highlightTerms) => { listItem.appendChild(document.createElement("span")).innerHTML = " (" + descr + ")"; // highlight search terms in the description - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); - } - else if (showSearchSummary) + if (SPHINX_HIGHLIGHT_ENABLED) + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + highlightTerms.forEach((term) => + _highlightText(listItem, term, "highlighted"), + ); + } else if (showSearchSummary) fetch(requestUrl) .then((responseData) => responseData.text()) .then((data) => { if (data) listItem.appendChild( - Search.makeSearchSummary(data, searchTerms, anchor) + Search.makeSearchSummary(data, searchTerms, anchor), ); // highlight search terms in the summary - if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js - highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + if (SPHINX_HIGHLIGHT_ENABLED) + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + highlightTerms.forEach((term) => + _highlightText(listItem, term, "highlighted"), + ); }); Search.output.appendChild(listItem); }; @@ -117,14 +123,14 @@ const _finishSearch = (resultCount) => { Search.title.innerText = _("Search Results"); if (!resultCount) Search.status.innerText = Documentation.gettext( - "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.", ); else Search.status.innerText = Documentation.ngettext( "Search finished, found one page matching the search query.", "Search finished, found ${resultCount} pages matching the search query.", resultCount, - ).replace('${resultCount}', resultCount); + ).replace("${resultCount}", resultCount); }; const _displayNextItem = ( results, @@ -138,7 +144,7 @@ const _displayNextItem = ( _displayItem(results.pop(), searchTerms, highlightTerms); setTimeout( () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), - 5 + 5, ); } // search finished, update title and status message @@ -170,9 +176,10 @@ const _orderResultsByScoreThenName = (a, b) => { * This is the same as ``\W+`` in Python, preserving the surrogate pair area. */ if (typeof splitQuery === "undefined") { - var splitQuery = (query) => query + var splitQuery = (query) => + query .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) - .filter(term => term) // remove remaining empty strings + .filter((term) => term); // remove remaining empty strings } /** @@ -184,16 +191,23 @@ const Search = { _pulse_status: -1, htmlToText: (htmlString, anchor) => { - const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + const htmlElement = new DOMParser().parseFromString( + htmlString, + "text/html", + ); for (const removalQuery of [".headerlink", "script", "style"]) { - htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + htmlElement.querySelectorAll(removalQuery).forEach((el) => { + el.remove(); + }); } if (anchor) { - const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + const anchorContent = htmlElement.querySelector( + `[role="main"] ${anchor}`, + ); if (anchorContent) return anchorContent.textContent; console.warn( - `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`, ); } @@ -202,7 +216,7 @@ const Search = { if (docContent) return docContent.textContent; console.warn( - "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template.", ); return ""; }, @@ -288,8 +302,7 @@ const Search = { // maybe skip this "word" // stopwords set is from language_data.js - if (stopwords.has(queryTermLower) || queryTerm.match(/^\d+$/)) - return; + if (stopwords.has(queryTermLower) || queryTerm.match(/^\d+$/)) return; // stem the word let word = stemmer.stemWord(queryTermLower); @@ -301,8 +314,12 @@ const Search = { } }); - if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js - localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + if (SPHINX_HIGHLIGHT_ENABLED) { + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + localStorage.setItem( + "sphinx_highlight_terms", + [...highlightTerms].join(" "), + ); } // console.debug("SEARCH: searching for:"); @@ -315,7 +332,13 @@ const Search = { /** * execute search (requires search index to be loaded) */ - _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + _performSearch: ( + query, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ) => { const filenames = Search._index.filenames; const docNames = Search._index.docnames; const titles = Search._index.titles; @@ -331,10 +354,15 @@ const Search = { const queryLower = query.toLowerCase().trim(); for (const [title, foundTitles] of Object.entries(allTitles)) { - if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + if ( + title.toLowerCase().trim().includes(queryLower) + && queryLower.length >= title.length / 2 + ) { for (const [file, id] of foundTitles) { - const score = Math.round(Scorer.title * queryLower.length / title.length); - const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + const score = Math.round( + (Scorer.title * queryLower.length) / title.length, + ); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles normalResults.push([ docNames[file], titles[file] !== title ? `${titles[file]} > ${title}` : title, @@ -350,9 +378,9 @@ const Search = { // search for explicit entries in index directives for (const [entry, foundEntries] of Object.entries(indexEntries)) { - if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + if (entry.includes(queryLower) && queryLower.length >= entry.length / 2) { for (const [file, id, isMain] of foundEntries) { - const score = Math.round(100 * queryLower.length / entry.length); + const score = Math.round((100 * queryLower.length) / entry.length); const result = [ docNames[file], titles[file], @@ -373,11 +401,13 @@ const Search = { // lookup as object objectTerms.forEach((term) => - normalResults.push(...Search.performObjectSearch(term, objectTerms)) + normalResults.push(...Search.performObjectSearch(term, objectTerms)), ); // lookup as search terms in fulltext - normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + normalResults.push( + ...Search.performTermsSearch(searchTerms, excludedTerms), + ); // let the scorer override scores with a custom scoring function if (Scorer.score) { @@ -398,7 +428,11 @@ const Search = { // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept let seen = new Set(); results = results.reverse().reduce((acc, result) => { - let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + let resultStr = result + .slice(0, 4) + .concat([result[5]]) + .map((v) => String(v)) + .join(","); if (!seen.has(resultStr)) { acc.push(result); seen.add(resultStr); @@ -410,8 +444,20 @@ const Search = { }, query: (query) => { - const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); - const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + const [ + searchQuery, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ] = Search._parseQuery(query); + const results = Search._performSearch( + searchQuery, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ); // for debugging //Search.lastresults = results.slice(); // a copy @@ -434,7 +480,7 @@ const Search = { const results = []; const objectSearchCallback = (prefix, match) => { - const name = match[4] + const name = match[4]; const fullname = (prefix ? prefix + "." : "") + name; const fullnameLower = fullname.toLowerCase(); if (fullnameLower.indexOf(object) < 0) return; @@ -486,9 +532,7 @@ const Search = { ]); }; Object.keys(objects).forEach((prefix) => - objects[prefix].forEach((array) => - objectSearchCallback(prefix, array) - ) + objects[prefix].forEach((array) => objectSearchCallback(prefix, array)), ); return results; }, @@ -513,8 +557,14 @@ const Search = { // find documents, if any, containing the query word in their text/title term indices // use Object.hasOwnProperty to avoid mismatching against prototype properties const arr = [ - { files: terms.hasOwnProperty(word) ? terms[word] : undefined, score: Scorer.term }, - { files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, score: Scorer.title }, + { + files: terms.hasOwnProperty(word) ? terms[word] : undefined, + score: Scorer.term, + }, + { + files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, + score: Scorer.title, + }, ]; // add support for partial matches if (word.length > 2) { @@ -555,7 +605,8 @@ const Search = { // create the mapping files.forEach((file) => { if (!fileMap.has(file)) fileMap.set(file, [word]); - else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + else if (fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); }); }); @@ -566,11 +617,11 @@ const Search = { // as search terms with length < 3 are discarded const filteredTermCount = [...searchTerms].filter( - (term) => term.length > 2 + (term) => term.length > 2, ).length; if ( - wordList.length !== searchTerms.size && - wordList.length !== filteredTermCount + wordList.length !== searchTerms.size + && wordList.length !== filteredTermCount ) continue; @@ -578,10 +629,10 @@ const Search = { if ( [...excludedTerms].some( (term) => - terms[term] === file || - titleTerms[term] === file || - (terms[term] || []).includes(file) || - (titleTerms[term] || []).includes(file) + terms[term] === file + || titleTerms[term] === file + || (terms[term] || []).includes(file) + || (titleTerms[term] || []).includes(file), ) ) break; @@ -623,7 +674,8 @@ const Search = { let summary = document.createElement("p"); summary.classList.add("context"); - summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + summary.textContent = + top + text.substr(startWithContext, 240).trim() + tail; return summary; }, diff --git a/sphinx/themes/basic/static/sphinx_highlight.js b/sphinx/themes/basic/static/sphinx_highlight.js index 8a96c69a194..ce735d52ee4 100644 --- a/sphinx/themes/basic/static/sphinx_highlight.js +++ b/sphinx/themes/basic/static/sphinx_highlight.js @@ -1,7 +1,7 @@ /* Highlighting utilities for Sphinx HTML documentation. */ "use strict"; -const SPHINX_HIGHLIGHT_ENABLED = true +const SPHINX_HIGHLIGHT_ENABLED = true; /** * highlight a given string on a node by wrapping it in @@ -13,9 +13,9 @@ const _highlight = (node, addItems, text, className) => { const parent = node.parentNode; const pos = val.toLowerCase().indexOf(text); if ( - pos >= 0 && - !parent.classList.contains(className) && - !parent.classList.contains("nohighlight") + pos >= 0 + && !parent.classList.contains(className) + && !parent.classList.contains("nohighlight") ) { let span; @@ -30,13 +30,7 @@ const _highlight = (node, addItems, text, className) => { span.appendChild(document.createTextNode(val.substr(pos, text.length))); const rest = document.createTextNode(val.substr(pos + text.length)); - parent.insertBefore( - span, - parent.insertBefore( - rest, - node.nextSibling - ) - ); + parent.insertBefore(span, parent.insertBefore(rest, node.nextSibling)); node.nodeValue = val.substr(0, pos); /* There may be more occurrences of search term in this node. So call this * function recursively on the remaining fragment. @@ -46,7 +40,7 @@ const _highlight = (node, addItems, text, className) => { if (isInSVG) { const rect = document.createElementNS( "http://www.w3.org/2000/svg", - "rect" + "rect", ); const bbox = parent.getBBox(); rect.x.baseVal.value = bbox.x; @@ -65,7 +59,7 @@ const _highlightText = (thisNode, text, className) => { let addItems = []; _highlight(thisNode, addItems, text, className); addItems.forEach((obj) => - obj.parent.insertAdjacentElement("beforebegin", obj.target) + obj.parent.insertAdjacentElement("beforebegin", obj.target), ); }; @@ -73,25 +67,27 @@ const _highlightText = (thisNode, text, className) => { * Small JavaScript module for the documentation. */ const SphinxHighlight = { - /** * highlight the search words provided in localstorage in the text */ highlightSearchWords: () => { - if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight // get and clear terms from localstorage const url = new URL(window.location); const highlight = - localStorage.getItem("sphinx_highlight_terms") - || url.searchParams.get("highlight") - || ""; - localStorage.removeItem("sphinx_highlight_terms") + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms"); url.searchParams.delete("highlight"); window.history.replaceState({}, "", url); // get individual terms from highlight string - const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + const terms = highlight + .toLowerCase() + .split(/\s+/) + .filter((x) => x); if (terms.length === 0) return; // nothing to do // There should never be more than one element matching "div.body" @@ -107,11 +103,11 @@ const SphinxHighlight = { document .createRange() .createContextualFragment( - '
' + - '' + - _("Hide Search Matches") + - "
" - ) + '' + + '' + + _("Hide Search Matches") + + "
", + ), ); }, @@ -125,7 +121,7 @@ const SphinxHighlight = { document .querySelectorAll("span.highlighted") .forEach((el) => el.classList.remove("highlighted")); - localStorage.removeItem("sphinx_highlight_terms") + localStorage.removeItem("sphinx_highlight_terms"); }, initEscapeListener: () => { @@ -134,10 +130,15 @@ const SphinxHighlight = { document.addEventListener("keydown", (event) => { // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) + return; // bail with special keys - if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; - if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) + return; + if ( + DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + && event.key === "Escape" + ) { SphinxHighlight.hideSearchWords(); event.preventDefault(); } diff --git a/sphinx/themes/scrolls/static/theme_extras.js b/sphinx/themes/scrolls/static/theme_extras.js index df2be407339..84cc1509808 100644 --- a/sphinx/themes/scrolls/static/theme_extras.js +++ b/sphinx/themes/scrolls/static/theme_extras.js @@ -1,12 +1,12 @@ const initialiseThemeExtras = () => { - const toc = document.getElementById("toc") - toc.style.display = "" - const items = toc.getElementsByTagName("ul")[0] - items.style.display = "none" - toc.getElementsByTagName("h3").addEventListener("click", () => { - if (items.style.display !== "none") toc.classList.remove("expandedtoc") - else toc.classList.add("expandedtoc"); - }) -} -if (document.readyState !== "loading") initialiseThemeExtras() -else document.addEventListener("DOMContentLoaded", initialiseThemeExtras) + const toc = document.getElementById("toc"); + toc.style.display = ""; + const items = toc.getElementsByTagName("ul")[0]; + items.style.display = "none"; + toc.getElementsByTagName("h3").addEventListener("click", () => { + if (items.style.display !== "none") toc.classList.remove("expandedtoc"); + else toc.classList.add("expandedtoc"); + }); +}; +if (document.readyState !== "loading") initialiseThemeExtras(); +else document.addEventListener("DOMContentLoaded", initialiseThemeExtras); diff --git a/tests/js/jasmine-browser.mjs b/tests/js/jasmine-browser.mjs index b84217fd8c5..f11c04b95b7 100644 --- a/tests/js/jasmine-browser.mjs +++ b/tests/js/jasmine-browser.mjs @@ -1,28 +1,26 @@ export default { srcDir: ".", srcFiles: [ - 'sphinx/themes/basic/static/doctools.js', - 'sphinx/themes/basic/static/searchtools.js', - 'sphinx/themes/basic/static/sphinx_highlight.js', - 'tests/js/fixtures/**/*.js', - 'tests/js/documentation_options.js', - 'tests/js/language_data.js', + "sphinx/themes/basic/static/doctools.js", + "sphinx/themes/basic/static/searchtools.js", + "sphinx/themes/basic/static/sphinx_highlight.js", + "tests/js/fixtures/**/*.js", + "tests/js/documentation_options.js", + "tests/js/language_data.js", ], specDir: "tests/js", - specFiles: [ - '**/*.spec.js', - ], + specFiles: ["**/*.spec.js"], helpers: [], env: { stopSpecOnExpectationFailure: false, stopOnSpecFailure: false, - random: true + random: true, }, listenAddress: "127.0.0.1", hostname: "127.0.0.1", browser: { - name: "headlessFirefox" - } + name: "headlessFirefox", + }, }; diff --git a/tests/js/language_data.js b/tests/js/language_data.js index 47c81f4a2a5..c98e91ff6e2 100644 --- a/tests/js/language_data.js +++ b/tests/js/language_data.js @@ -4,17 +4,15 @@ */ const stopwords = new Set([]); -window.stopwords = stopwords; // Export to global scope - +window.stopwords = stopwords; // Export to global scope /* Non-minified versions are copied as separate JavaScript files, if available */ /** * Dummy stemmer for languages without stemming rules. */ -var Stemmer = function() { - this.stemWord = function(w) { +var Stemmer = function () { + this.stemWord = function (w) { return w; - } -} - + }; +}; diff --git a/tests/js/searchtools.spec.js b/tests/js/searchtools.spec.js index 809fd19d0f4..d00689c907c 100644 --- a/tests/js/searchtools.spec.js +++ b/tests/js/searchtools.spec.js @@ -1,10 +1,9 @@ -describe('Basic html theme search', function() { - +describe("Basic html theme search", function () { function loadFixture(name) { - req = new XMLHttpRequest(); - req.open("GET", `__src__/tests/js/fixtures/${name}`, false); - req.send(null); - return req.responseText; + req = new XMLHttpRequest(); + req.open("GET", `__src__/tests/js/fixtures/${name}`, false); + req.send(null); + return req.responseText; } function checkRanking(expectedRanking, results) { @@ -16,7 +15,11 @@ describe('Basic html theme search', function() { let [expectedPage, expectedTitle, expectedTarget] = nextExpected; let [page, title, target] = result; - if (page == expectedPage && title == expectedTitle && target == expectedTarget) { + if ( + page == expectedPage + && title == expectedTitle + && target == expectedTarget + ) { [nextExpected, ...remainingItems] = remainingItems; } } @@ -25,13 +28,14 @@ describe('Basic html theme search', function() { expect(nextExpected).toEqual(undefined); } - describe('terms search', function() { - - it('should find "C++" when in index', function() { + describe("terms search", function () { + it('should find "C++" when in index', function () { eval(loadFixture("cpp/searchindex.js")); - [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('C++'); + [_searchQuery, searchterms, excluded, ..._remainingItems] = + Search._parseQuery("C++"); + // prettier-ignore hits = [[ "index", "<no title>", @@ -44,10 +48,12 @@ describe('Basic html theme search', function() { expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); - it('should be able to search for multiple terms', function() { + it("should be able to search for multiple terms", function () { eval(loadFixture("multiterm/searchindex.js")); - [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('main page'); + [_searchQuery, searchterms, excluded, ..._remainingItems] = + Search._parseQuery("main page"); + // prettier-ignore hits = [[ 'index', 'Main Page', @@ -60,11 +66,13 @@ describe('Basic html theme search', function() { expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); - it('should partially-match "sphinx" when in title index', function() { + it('should partially-match "sphinx" when in title index', function () { eval(loadFixture("partial/searchindex.js")); - [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('sphinx'); + [_searchQuery, searchterms, excluded, ..._remainingItems] = + Search._parseQuery("sphinx"); + // prettier-ignore hits = [[ "index", "sphinx_utils module", @@ -77,13 +85,15 @@ describe('Basic html theme search', function() { expect(Search.performTermsSearch(searchterms, excluded)).toEqual(hits); }); - it('should partially-match within "possible" when in term index', function() { + it('should partially-match within "possible" when in term index', function () { eval(loadFixture("partial/searchindex.js")); - [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('ossibl'); + [_searchQuery, searchterms, excluded, ..._remainingItems] = + Search._parseQuery("ossibl"); terms = Search._index.terms; titleterms = Search._index.titleterms; + // prettier-ignore hits = [[ "index", "sphinx_utils module", @@ -93,18 +103,19 @@ describe('Basic html theme search', function() { "index.rst", "text" ]]; - expect(Search.performTermsSearch(searchterms, excluded, terms, titleterms)).toEqual(hits); + expect( + Search.performTermsSearch(searchterms, excluded, terms, titleterms), + ).toEqual(hits); }); - }); - describe('aggregation of search results', function() { - - it('should combine document title and document term matches', function() { + describe("aggregation of search results", function () { + it("should combine document title and document term matches", function () { eval(loadFixture("multiterm/searchindex.js")); - searchParameters = Search._parseQuery('main page'); + searchParameters = Search._parseQuery("main page"); + // prettier-ignore hits = [ [ 'index', @@ -118,11 +129,9 @@ describe('Basic html theme search', function() { ]; expect(Search._performSearch(...searchParameters)).toEqual(hits); }); - }); - describe('search result ranking', function() { - + describe("search result ranking", function () { /* * These tests should not proscribe precise expected ordering of search * results; instead each test case should describe a single relevance rule @@ -137,95 +146,96 @@ describe('Basic html theme search', function() { * [1] - https://github.com/sphinx-doc/sphinx.git/ */ - it('should score a code module match above a page-title match', function() { + it("should score a code module match above a page-title match", function () { eval(loadFixture("titles/searchindex.js")); + // prettier-ignore expectedRanking = [ ['index', 'relevance', '#module-relevance'], /* py:module documentation */ ['relevance', 'Relevance', ''], /* main title */ ]; - searchParameters = Search._parseQuery('relevance'); + searchParameters = Search._parseQuery("relevance"); results = Search._performSearch(...searchParameters); checkRanking(expectedRanking, results); }); - it('should score a main-title match above an object member match', function() { + it("should score a main-title match above an object member match", function () { eval(loadFixture("titles/searchindex.js")); + // prettier-ignore expectedRanking = [ ['relevance', 'Relevance', ''], /* main title */ ['index', 'relevance.Example.relevance', '#relevance.Example.relevance'], /* py:class attribute */ ]; - searchParameters = Search._parseQuery('relevance'); + searchParameters = Search._parseQuery("relevance"); results = Search._performSearch(...searchParameters); checkRanking(expectedRanking, results); }); - it('should score a title match above a standard index entry match', function() { + it("should score a title match above a standard index entry match", function () { eval(loadFixture("titles/searchindex.js")); + // prettier-ignore expectedRanking = [ ['relevance', 'Relevance', ''], /* title */ ['index', 'Main Page', '#index-1'], /* index entry */ ]; - searchParameters = Search._parseQuery('relevance'); + searchParameters = Search._parseQuery("relevance"); results = Search._performSearch(...searchParameters); checkRanking(expectedRanking, results); }); - it('should score a priority index entry match above a title match', function() { + it("should score a priority index entry match above a title match", function () { eval(loadFixture("titles/searchindex.js")); + // prettier-ignore expectedRanking = [ ['index', 'Main Page', '#index-0'], /* index entry */ ['index', 'Main Page > Result Scoring', '#result-scoring'], /* title */ ]; - searchParameters = Search._parseQuery('scoring'); + searchParameters = Search._parseQuery("scoring"); results = Search._performSearch(...searchParameters); checkRanking(expectedRanking, results); }); - it('should score a main-title match above a subheading-title match', function() { + it("should score a main-title match above a subheading-title match", function () { eval(loadFixture("titles/searchindex.js")); + // prettier-ignore expectedRanking = [ ['relevance', 'Relevance', ''], /* main title */ ['index', 'Main Page > Relevance', '#relevance'], /* subsection heading title */ ]; - searchParameters = Search._parseQuery('relevance'); + searchParameters = Search._parseQuery("relevance"); results = Search._performSearch(...searchParameters); checkRanking(expectedRanking, results); }); - }); - describe('can handle edge-case search queries', function() { - - it('does not find the javascript prototype property in unrelated documents', function() { + describe("can handle edge-case search queries", function () { + it("does not find the javascript prototype property in unrelated documents", function () { eval(loadFixture("partial/searchindex.js")); - searchParameters = Search._parseQuery('__proto__'); + searchParameters = Search._parseQuery("__proto__"); + // prettier-ignore hits = []; expect(Search._performSearch(...searchParameters)).toEqual(hits); }); - }); - }); -describe("htmlToText", function() { - +describe("htmlToText", function () { const testHTML = ` @@ -257,44 +267,47 @@ describe("htmlToText", function() { `; it("basic case", () => { - expect(Search.htmlToText(testHTML).trim().split(/\s+/)).toEqual([ - 'Getting', 'Started', 'Some', 'text', - 'Other', 'Section', 'Other', 'text', - 'Yet', 'Another', 'Section', 'More', 'text' - ]); + expect(Search.htmlToText(testHTML).trim().split(/\s+/)).toEqual( + /* prettier-ignore */ [ + "Getting", "Started", "Some", "text", + "Other", "Section", "Other", "text", + "Yet", "Another", "Section", "More", "text" + ], + ); }); it("will start reading from the anchor", () => { - expect(Search.htmlToText(testHTML, '#other-section').trim().split(/\s+/)).toEqual(['Other', 'Section', 'Other', 'text']); + expect( + Search.htmlToText(testHTML, "#other-section").trim().split(/\s+/), + ).toEqual(["Other", "Section", "Other", "text"]); }); }); // Regression test for https://github.com/sphinx-doc/sphinx/issues/3150 -describe('splitQuery regression tests', () => { - - it('can split English words', () => { - const parts = splitQuery(' Hello World ') - expect(parts).toEqual(['Hello', 'World']) - }) - - it('can split special characters', () => { - const parts = splitQuery('Pin-Code') - expect(parts).toEqual(['Pin', 'Code']) - }) - - it('can split Chinese characters', () => { - const parts = splitQuery('Hello from 中国 上海') - expect(parts).toEqual(['Hello', 'from', '中国', '上海']) - }) - - it('can split Emoji (surrogate pair) characters. It should keep emojis.', () => { - const parts = splitQuery('😁😁') - expect(parts).toEqual(['😁😁']) - }) - - it('can split umlauts. It should keep umlauts.', () => { - const parts = splitQuery('Löschen Prüfung Abändern ærlig spørsmål') - expect(parts).toEqual(['Löschen', 'Prüfung', 'Abändern', 'ærlig', 'spørsmål']) - }) - -}) +describe("splitQuery regression tests", () => { + it("can split English words", () => { + const parts = splitQuery(" Hello World "); + expect(parts).toEqual(["Hello", "World"]); + }); + + it("can split special characters", () => { + const parts = splitQuery("Pin-Code"); + expect(parts).toEqual(["Pin", "Code"]); + }); + + it("can split Chinese characters", () => { + const parts = splitQuery("Hello from 中国 上海"); + expect(parts).toEqual(["Hello", "from", "中国", "上海"]); + }); + + it("can split Emoji (surrogate pair) characters. It should keep emojis.", () => { + const parts = splitQuery("😁😁"); + expect(parts).toEqual(["😁😁"]); + }); + + it("can split umlauts. It should keep umlauts.", () => { + const parts = splitQuery("Löschen Prüfung Abändern ærlig spørsmål"); + // prettier-ignore + expect(parts).toEqual(["Löschen", "Prüfung", "Abändern", "ærlig", "spørsmål"]) + }); +}); diff --git a/tests/js/sphinx_highlight.spec.js b/tests/js/sphinx_highlight.spec.js index 1f52eabb96f..4d57d867745 100644 --- a/tests/js/sphinx_highlight.spec.js +++ b/tests/js/sphinx_highlight.spec.js @@ -1,30 +1,33 @@ -describe('highlightText', function() { +describe("highlightText", function () { + const cyrillicTerm = "шеллы"; + const umlautTerm = "gänsefüßchen"; - const cyrillicTerm = 'шеллы'; - const umlautTerm = 'gänsefüßchen'; - - it('should highlight text incl. special characters correctly in HTML', function() { + it("should highlight text incl. special characters correctly in HTML", function () { const highlightTestSpan = new DOMParser().parseFromString( - 'This is the шеллы and Gänsefüßchen test!', 'text/html').body.firstChild - _highlightText(highlightTestSpan, cyrillicTerm, 'highlighted'); - _highlightText(highlightTestSpan, umlautTerm, 'highlighted'); + "This is the шеллы and Gänsefüßchen test!", + "text/html", + ).body.firstChild; + _highlightText(highlightTestSpan, cyrillicTerm, "highlighted"); + _highlightText(highlightTestSpan, umlautTerm, "highlighted"); const expectedHtmlString = - 'This is the шеллы and ' + - 'Gänsefüßchen test!'; + 'This is the шеллы and ' + + 'Gänsefüßchen test!'; expect(highlightTestSpan.innerHTML).toEqual(expectedHtmlString); }); - it('should highlight text incl. special characters correctly in SVG', function() { + it("should highlight text incl. special characters correctly in SVG", function () { const highlightTestSvg = new DOMParser().parseFromString( - '' + - '' + - '', 'text/html').body.firstChild - _highlightText(highlightTestSvg, cyrillicTerm, 'highlighted'); - _highlightText(highlightTestSvg, umlautTerm, 'highlighted'); + '' + + '" + + "", + "text/html", + ).body.firstChild; + _highlightText(highlightTestSvg, cyrillicTerm, "highlighted"); + _highlightText(highlightTestSvg, umlautTerm, "highlighted"); /* Note wild cards and ``toMatch``; allowing for some variability seems to be necessary, even between different FF versions */ const expectedSvgString = @@ -32,8 +35,9 @@ describe('highlightText', function() { + '