summaryrefslogtreecommitdiffstats
path: root/tests/js
diff options
context:
space:
mode:
Diffstat (limited to 'tests/js')
-rw-r--r--tests/js/fixtures/cpp/searchindex.js1
-rw-r--r--tests/js/fixtures/multiterm/searchindex.js1
-rw-r--r--tests/js/fixtures/partial/searchindex.js1
-rw-r--r--tests/js/fixtures/titles/searchindex.js1
-rw-r--r--tests/js/language_data.js26
-rw-r--r--tests/js/roots/cpp/conf.py0
-rw-r--r--tests/js/roots/cpp/index.rst10
-rw-r--r--tests/js/roots/multiterm/conf.py0
-rw-r--r--tests/js/roots/multiterm/index.rst13
-rw-r--r--tests/js/roots/partial/conf.py0
-rw-r--r--tests/js/roots/partial/index.rst9
-rw-r--r--tests/js/roots/titles/conf.py6
-rw-r--r--tests/js/roots/titles/index.rst20
-rw-r--r--tests/js/roots/titles/relevance.py7
-rw-r--r--tests/js/roots/titles/relevance.rst13
-rw-r--r--tests/js/searchtools.js166
16 files changed, 232 insertions, 42 deletions
diff --git a/tests/js/fixtures/cpp/searchindex.js b/tests/js/fixtures/cpp/searchindex.js
new file mode 100644
index 0000000..46f4824
--- /dev/null
+++ b/tests/js/fixtures/cpp/searchindex.js
@@ -0,0 +1 @@
+Search.setIndex({"alltitles": {}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {"sphinx (c++ class)": [[0, "_CPPv46Sphinx", false]]}, "objects": {"": [[0, 0, 1, "_CPPv46Sphinx", "Sphinx"]]}, "objnames": {"0": ["cpp", "class", "C++ class"]}, "objtypes": {"0": "cpp:class"}, "terms": {"The": 0, "becaus": 0, "c": 0, "can": 0, "cardin": 0, "challeng": 0, "charact": 0, "class": 0, "descript": 0, "drop": 0, "engin": 0, "fixtur": 0, "frequent": 0, "gener": 0, "i": 0, "index": 0, "inflat": 0, "mathemat": 0, "occur": 0, "often": 0, "project": 0, "punctuat": 0, "queri": 0, "relat": 0, "sampl": 0, "search": 0, "size": 0, "sphinx": 0, "term": 0, "thei": 0, "thi": 0, "token": 0, "us": 0, "web": 0, "would": 0}, "titles": ["<no title>"], "titleterms": {}}) \ No newline at end of file
diff --git a/tests/js/fixtures/multiterm/searchindex.js b/tests/js/fixtures/multiterm/searchindex.js
new file mode 100644
index 0000000..a868eb6
--- /dev/null
+++ b/tests/js/fixtures/multiterm/searchindex.js
@@ -0,0 +1 @@
+Search.setIndex({"alltitles": {"Main Page": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"At": 0, "adjac": 0, "all": 0, "an": 0, "appear": 0, "applic": 0, "ar": 0, "built": 0, "can": 0, "check": 0, "contain": 0, "do": 0, "document": 0, "doesn": 0, "each": 0, "fixtur": 0, "format": 0, "function": 0, "futur": 0, "html": 0, "i": 0, "includ": 0, "match": 0, "messag": 0, "multipl": 0, "multiterm": 0, "order": 0, "other": 0, "output": 0, "perform": 0, "perhap": 0, "phrase": 0, "project": 0, "queri": 0, "requir": 0, "same": 0, "search": 0, "successfulli": 0, "support": 0, "t": 0, "term": 0, "test": 0, "thi": 0, "time": 0, "us": 0, "when": 0, "write": 0}, "titles": ["Main Page"], "titleterms": {"main": 0, "page": 0}}) \ No newline at end of file
diff --git a/tests/js/fixtures/partial/searchindex.js b/tests/js/fixtures/partial/searchindex.js
new file mode 100644
index 0000000..356386a
--- /dev/null
+++ b/tests/js/fixtures/partial/searchindex.js
@@ -0,0 +1 @@
+Search.setIndex({"alltitles": {"sphinx_utils module": [[0, null]]}, "docnames": ["index"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"also": 0, "ar": 0, "built": 0, "confirm": 0, "document": 0, "function": 0, "html": 0, "i": 0, "includ": 0, "input": 0, "javascript": 0, "known": 0, "match": 0, "partial": 0, "possibl": 0, "prefix": 0, "project": 0, "provid": 0, "restructuredtext": 0, "sampl": 0, "search": 0, "should": 0, "thi": 0, "titl": 0, "us": 0, "when": 0}, "titles": ["sphinx_utils module"], "titleterms": {"modul": 0, "sphinx_util": 0}}) \ No newline at end of file
diff --git a/tests/js/fixtures/titles/searchindex.js b/tests/js/fixtures/titles/searchindex.js
new file mode 100644
index 0000000..9a229d0
--- /dev/null
+++ b/tests/js/fixtures/titles/searchindex.js
@@ -0,0 +1 @@
+Search.setIndex({"alltitles": {"Main Page": [[0, null]], "Relevance": [[0, "relevance"], [1, null]]}, "docnames": ["index", "relevance"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["index.rst", "relevance.rst"], "indexentries": {"example (class in relevance)": [[0, "relevance.Example", false]], "module": [[0, "module-relevance", false]], "relevance": [[0, "module-relevance", false]], "relevance (relevance.example attribute)": [[0, "relevance.Example.relevance", false]]}, "objects": {"": [[0, 0, 0, "-", "relevance"]], "relevance": [[0, 1, 1, "", "Example"]], "relevance.Example": [[0, 2, 1, "", "relevance"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "attribute", "Python attribute"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:attribute"}, "terms": {"": [0, 1], "A": 1, "For": 1, "In": [0, 1], "against": 0, "also": 1, "an": 0, "answer": 0, "appear": 1, "ar": 1, "area": 0, "ask": 0, "attribut": 0, "built": 1, "can": [0, 1], "class": 0, "code": [0, 1], "consid": 1, "contain": 0, "context": 0, "corpu": 1, "could": 1, "demonstr": 0, "describ": 1, "detail": 1, "determin": 1, "docstr": 0, "document": [0, 1], "domain": 1, "engin": 0, "exampl": [0, 1], "extract": 0, "find": 0, "found": 0, "from": 0, "function": 1, "ha": 1, "handl": 0, "happen": 1, "head": 0, "help": 0, "highli": 1, "how": 0, "i": [0, 1], "improv": 0, "inform": 0, "intend": 0, "issu": 1, "itself": 1, "knowledg": 0, "languag": 1, "less": 1, "like": [0, 1], "match": 0, "mention": 1, "name": [0, 1], "object": 0, "one": 1, "onli": 1, "other": 0, "page": 1, "part": 1, "particular": 0, "printf": 1, "program": 1, "project": 0, "queri": [0, 1], "question": 0, "re": 0, "rel": 0, "research": 0, "result": 1, "sai": 0, "same": 1, "score": 0, "search": [0, 1], "seem": 0, "softwar": 1, "some": 1, "sphinx": 0, "straightforward": 1, "subject": 0, "subsect": 0, "term": [0, 1], "test": 0, "text": 0, "than": 1, "thei": 0, "them": 0, "thi": 0, "titl": 0, "user": [0, 1], "we": [0, 1], "when": 0, "whether": 1, "within": 0, "would": 1}, "titles": ["Main Page", "Relevance"], "titleterms": {"main": 0, "page": 0, "relev": [0, 1]}}) \ No newline at end of file
diff --git a/tests/js/language_data.js b/tests/js/language_data.js
new file mode 100644
index 0000000..89083d9
--- /dev/null
+++ b/tests/js/language_data.js
@@ -0,0 +1,26 @@
+/*
+ * language_data.js
+ * ~~~~~~~~~~~~~~~~
+ *
+ * This script contains the language-specific data used by searchtools.js,
+ * namely the list of stopwords, stemmer, scorer and splitter.
+ *
+ * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+var stopwords = [];
+
+
+/* Non-minified version is copied as a separate JS file, if available */
+
+/**
+ * Dummy stemmer for languages without stemming rules.
+ */
+var Stemmer = function() {
+ this.stemWord = function(w) {
+ return w;
+ }
+}
+
diff --git a/tests/js/roots/cpp/conf.py b/tests/js/roots/cpp/conf.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/js/roots/cpp/conf.py
diff --git a/tests/js/roots/cpp/index.rst b/tests/js/roots/cpp/index.rst
new file mode 100644
index 0000000..d731343
--- /dev/null
+++ b/tests/js/roots/cpp/index.rst
@@ -0,0 +1,10 @@
+This is a sample C++ project used to generate a search engine index fixture.
+
+.. cpp:class:: public Sphinx
+
+ The description of Sphinx class.
+
+Indexing and querying the term C++ can be challenging, because search-related
+tokenization often drops punctuation and mathematical characters (they occur
+frequently on the web and would inflate the cardinality and size of web search
+indexes).
diff --git a/tests/js/roots/multiterm/conf.py b/tests/js/roots/multiterm/conf.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/js/roots/multiterm/conf.py
diff --git a/tests/js/roots/multiterm/index.rst b/tests/js/roots/multiterm/index.rst
new file mode 100644
index 0000000..495e5ce
--- /dev/null
+++ b/tests/js/roots/multiterm/index.rst
@@ -0,0 +1,13 @@
+Main Page
+=========
+
+This is the main page of the ``multiterm`` test project.
+
+This document is used as a test fixture to check that the search functionality
+included when projects are built into an HTML output format can successfully
+match this document when a search query containing multiple terms is performed.
+
+At the time-of-writing this message, the application doesn't support "phrase
+queries" -- queries that require all of the contained terms to appear adjacent
+to each other and in the same order in the document as in the query; perhaps it
+will do in future?
diff --git a/tests/js/roots/partial/conf.py b/tests/js/roots/partial/conf.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/js/roots/partial/conf.py
diff --git a/tests/js/roots/partial/index.rst b/tests/js/roots/partial/index.rst
new file mode 100644
index 0000000..6a9561b
--- /dev/null
+++ b/tests/js/roots/partial/index.rst
@@ -0,0 +1,9 @@
+sphinx_utils module
+===================
+
+Partial (also known as "prefix") matches on document titles should be possible
+using the JavaScript search functionality included when HTML documentation
+projects are built.
+
+This document provides a sample reStructuredText input to confirm that partial
+title matching is possible.
diff --git a/tests/js/roots/titles/conf.py b/tests/js/roots/titles/conf.py
new file mode 100644
index 0000000..e5f6bb9
--- /dev/null
+++ b/tests/js/roots/titles/conf.py
@@ -0,0 +1,6 @@
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath('.'))
+
+extensions = ['sphinx.ext.autodoc']
diff --git a/tests/js/roots/titles/index.rst b/tests/js/roots/titles/index.rst
new file mode 100644
index 0000000..464cd95
--- /dev/null
+++ b/tests/js/roots/titles/index.rst
@@ -0,0 +1,20 @@
+Main Page
+=========
+
+This is the main page of the ``titles`` test project.
+
+In particular, this test project is intended to demonstrate how Sphinx
+can handle scoring of query matches against document titles and subsection
+heading titles relative to other document matches such as terms found within
+document text and object names extracted from code.
+
+Relevance
+---------
+
+In the context of search engines, we can say that a document is **relevant**
+to a user's query when it contains information that seems likely to help them
+find an answer to a question they're asking, or to improve their knowledge of
+the subject area they're researching.
+
+.. automodule:: relevance
+ :members:
diff --git a/tests/js/roots/titles/relevance.py b/tests/js/roots/titles/relevance.py
new file mode 100644
index 0000000..c4d0eec
--- /dev/null
+++ b/tests/js/roots/titles/relevance.py
@@ -0,0 +1,7 @@
+class Example:
+ """Example class"""
+ num_attribute = 5
+ text_attribute = "string"
+
+ relevance = "testing"
+ """attribute docstring"""
diff --git a/tests/js/roots/titles/relevance.rst b/tests/js/roots/titles/relevance.rst
new file mode 100644
index 0000000..18f494f
--- /dev/null
+++ b/tests/js/roots/titles/relevance.rst
@@ -0,0 +1,13 @@
+Relevance
+=========
+
+In some domains, it can be straightforward to determine whether a search result
+is relevant to the user's query.
+
+For example, if we are in a software programming language domain, and a user
+has issued a query for the term ``printf``, then we could consider a document
+in the corpus that describes a built-in language function with the same name
+as (highly) relevant. A document that only happens to mention the ``printf``
+function name as part of some example code that appears on the page would
+also be relevant, but likely less relevant than the one that describes the
+function itself in detail.
diff --git a/tests/js/searchtools.js b/tests/js/searchtools.js
index 4f9984d..ebf37e5 100644
--- a/tests/js/searchtools.js
+++ b/tests/js/searchtools.js
@@ -1,20 +1,38 @@
describe('Basic html theme search', function() {
+ function loadFixture(name) {
+ req = new XMLHttpRequest();
+ req.open("GET", `base/tests/js/fixtures/${name}`, false);
+ req.send(null);
+ return req.responseText;
+ }
+
+ function checkRanking(expectedRanking, results) {
+ let [nextExpected, ...remainingItems] = expectedRanking;
+
+ for (result of results.reverse()) {
+ if (!nextExpected) break;
+
+ let [expectedPage, expectedTitle, expectedTarget] = nextExpected;
+ let [page, title, target] = result;
+
+ if (page == expectedPage && title == expectedTitle && target == expectedTarget) {
+ [nextExpected, ...remainingItems] = remainingItems;
+ }
+ }
+
+ expect(remainingItems.length).toEqual(0);
+ expect(nextExpected).toEqual(undefined);
+ }
+
describe('terms search', function() {
it('should find "C++" when in index', function() {
- index = {
- docnames:["index"],
- filenames:["index.rst"],
- terms:{'c++':0},
- titles:["<no title>"],
- titleterms:{}
- }
- Search.setIndex(index);
- searchterms = ['c++'];
- excluded = [];
- terms = index.terms;
- titleterms = index.titleterms;
+ eval(loadFixture("cpp/searchindex.js"));
+
+ [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('C++');
+ terms = Search._index.terms;
+ titleterms = Search._index.titleterms;
hits = [[
"index",
@@ -28,22 +46,11 @@ describe('Basic html theme search', function() {
});
it('should be able to search for multiple terms', function() {
- index = {
- alltitles: {
- 'Main Page': [[0, 'main-page']],
- },
- docnames:["index"],
- filenames:["index.rst"],
- terms:{main:0, page:0},
- titles:["Main Page"],
- titleterms:{ main:0, page:0 }
- }
- Search.setIndex(index);
+ eval(loadFixture("multiterm/searchindex.js"));
- searchterms = ['main', 'page'];
- excluded = [];
- terms = index.terms;
- titleterms = index.titleterms;
+ [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('main page');
+ terms = Search._index.terms;
+ titleterms = Search._index.titleterms;
hits = [[
'index',
'Main Page',
@@ -55,18 +62,11 @@ describe('Basic html theme search', function() {
});
it('should partially-match "sphinx" when in title index', function() {
- index = {
- docnames:["index"],
- filenames:["index.rst"],
- terms:{'useful': 0, 'utilities': 0},
- titles:["sphinx_utils module"],
- titleterms:{'sphinx_utils': 0}
- }
- Search.setIndex(index);
- searchterms = ['sphinx'];
- excluded = [];
- terms = index.terms;
- titleterms = index.titleterms;
+ eval(loadFixture("partial/searchindex.js"));
+
+ [_searchQuery, searchterms, excluded, ..._remainingItems] = Search._parseQuery('sphinx');
+ terms = Search._index.terms;
+ titleterms = Search._index.titleterms;
hits = [[
"index",
@@ -81,6 +81,88 @@ describe('Basic html theme search', 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');
+
+ hits = [
+ [
+ 'index',
+ 'Main Page',
+ '',
+ null,
+ 16,
+ 'index.rst'
+ ]
+ ];
+ expect(Search._performSearch(...searchParameters)).toEqual(hits);
+ });
+
+ });
+
+ 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
+ * that helps users to locate relevant information efficiently.
+ *
+ * If you think that one of the rules seems to be poorly-defined or is
+ * limiting the potential for search algorithm improvements, please check
+ * for existing discussion/bugreports related to it on GitHub[1] before
+ * creating one yourself. Suggestions for possible improvements are also
+ * welcome.
+ *
+ * [1] - https://github.com/sphinx-doc/sphinx.git/
+ */
+
+ it('should score a code module match above a page-title match', function() {
+ eval(loadFixture("titles/searchindex.js"));
+
+ expectedRanking = [
+ ['index', 'relevance', '#module-relevance'], /* py:module documentation */
+ ['relevance', 'Relevance', ''], /* main title */
+ ];
+
+ searchParameters = Search._parseQuery('relevance');
+ results = Search._performSearch(...searchParameters);
+
+ checkRanking(expectedRanking, results);
+ });
+
+ it('should score a main-title match above an object member match', function() {
+ eval(loadFixture("titles/searchindex.js"));
+
+ expectedRanking = [
+ ['relevance', 'Relevance', ''], /* main title */
+ ['index', 'relevance.Example.relevance', '#relevance.Example.relevance'], /* py:class attribute */
+ ];
+
+ searchParameters = Search._parseQuery('relevance');
+ results = Search._performSearch(...searchParameters);
+
+ checkRanking(expectedRanking, results);
+ });
+
+ it('should score a main-title match above a subheading-title match', function() {
+ eval(loadFixture("titles/searchindex.js"));
+
+ expectedRanking = [
+ ['relevance', 'Relevance', ''], /* main title */
+ ['index', 'Main Page > Relevance', '#relevance'], /* subsection heading title */
+ ];
+
+ searchParameters = Search._parseQuery('relevance');
+ results = Search._performSearch(...searchParameters);
+
+ checkRanking(expectedRanking, results);
+ });
+
+ });
+
});
describe("htmlToText", function() {
@@ -100,15 +182,15 @@ describe("htmlToText", function() {
</style>
<!-- main content -->
<section id="getting-started">
- <h1>Getting Started</h1>
+ <h1>Getting Started <a class="headerlink" href="#getting-started" title="Link to this heading">¶</a></h1>
<p>Some text</p>
</section>
<section id="other-section">
- <h1>Other Section</h1>
+ <h1>Other Section <a class="headerlink" href="#other-section" title="Link to this heading">¶</a></h1>
<p>Other text</p>
</section>
<section id="yet-another-section">
- <h1>Yet Another Section</h1>
+ <h1>Yet Another Section <a class="headerlink" href="#yet-another-section" title="Link to this heading">¶</a></h1>
<p>More text</p>
</section>
</div>