Adopt Prettier for JavaScript formatting (#13581)

This commit is contained in:
Adam Turner authored and GitHub committed 2025-05-24 02:15:04 +01:00
1 parent 1a69059295
commit 2c2159fb9e
12 files changed
+310 -217

No files matched your search

+18
View File
@@ -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/**"
+2
View File
@@ -0,0 +1,2 @@
# https://prettier.io/docs/options
experimentalOperatorPosition = "start"
+4 -4
View File
@@ -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+')
+6 -5
View File
@@ -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;
+103 -51
View File
@@ -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;
},
+30 -29
View File
@@ -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(
'<p class="highlight-link">' +
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
_("Hide Search Matches") +
"</a></p>"
)
'<p class="highlight-link">'
+ '<a href="javascript:SphinxHighlight.hideSearchWords()">'
+ _("Hide Search Matches")
+ "</a></p>",
),
);
},
@@ -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();
}
+11 -11
View File
@@ -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);
+10 -12
View File
@@ -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",
},
};
+5 -7
View File
@@ -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;
}
}
};
};
+88 -75
View File
@@ -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",
"&lt;no title&gt;",
@@ -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 = `<html>
<body>
<script src="directory/filename.js"></script>
@@ -257,44 +267,47 @@ describe("htmlToText", function() {
</html>`;
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', () => {
describe("splitQuery regression tests", () => {
it("can split English words", () => {
const parts = splitQuery(" Hello World ");
expect(parts).toEqual(["Hello", "World"]);
});
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 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 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 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'])
})
})
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"])
});
});
+27 -23
View File
@@ -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(
'<span>This is the шеллы and Gänsefüßchen test!</span>', 'text/html').body.firstChild
_highlightText(highlightTestSpan, cyrillicTerm, 'highlighted');
_highlightText(highlightTestSpan, umlautTerm, 'highlighted');
"<span>This is the шеллы and Gänsefüßchen test!</span>",
"text/html",
).body.firstChild;
_highlightText(highlightTestSpan, cyrillicTerm, "highlighted");
_highlightText(highlightTestSpan, umlautTerm, "highlighted");
const expectedHtmlString =
'This is the <span class=\"highlighted\">шеллы</span> and ' +
'<span class=\"highlighted\">Gänsefüßchen</span> test!';
'This is the <span class=\"highlighted\">шеллы</span> and '
+ '<span class=\"highlighted\">Gänsefüßchen</span> 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(
'<span id="svg-highlight-test">' +
'<svg xmlns="http://www.w3.org/2000/svg" height="50" width="500">' +
'<text x="0" y="15">' +
'This is the шеллы and Gänsefüßchen test!' +
'</text>' +
'</svg>' +
'</span>', 'text/html').body.firstChild
_highlightText(highlightTestSvg, cyrillicTerm, 'highlighted');
_highlightText(highlightTestSvg, umlautTerm, 'highlighted');
'<span id="svg-highlight-test">'
+ '<svg xmlns="http://www.w3.org/2000/svg" height="50" width="500">'
+ '<text x="0" y="15">'
+ "This is the шеллы and Gänsefüßchen test!"
+ "</text>"
+ "</svg>"
+ "</span>",
"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() {
+ '<rect x=".*" y=".*" width=".*" height=".*" class="highlighted"/>'
+ '<rect x=".*" y=".*" width=".*" height=".*" class="highlighted"/>'
+ '<text x=".*" y=".*">This is the <tspan>шеллы</tspan> and '
+ '<tspan>Gänsefüßchen</tspan> test!</text></svg>';
expect(new XMLSerializer().serializeToString(highlightTestSvg.firstChild)).toMatch(new RegExp(expectedSvgString));
+ "<tspan>Gänsefüßchen</tspan> test!</text></svg>";
expect(
new XMLSerializer().serializeToString(highlightTestSvg.firstChild),
).toMatch(new RegExp(expectedSvgString));
});
});
+6
View File
@@ -84,3 +84,9 @@ dependency_groups =
types
commands =
mypy {posargs}
[testenv:prettier]
description =
Run the Prettier JavaScript formatter.
commands =
npx prettier@3.5 --write "sphinx/themes/**/*.js" "!sphinx/themes/bizstyle/static/css3-mediaqueries*.js" "tests/js/**/*.{js,mjs}" "!tests/js/fixtures/**"