Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions website/static/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,11 +361,6 @@ document.addEventListener("click", function (e) {
gtag("event", "sponsor_contact", { tier: link.dataset.tier });
});

if (location.hash) {
const linkedRow = document.getElementById(decodeURIComponent(location.hash.slice(1)));
if (linkedRow && linkedRow.matches("tr.row")) openEntryRow(linkedRow);
}

if (searchInput) {
let searchTimer;
searchInput.addEventListener("input", function () {
Expand Down Expand Up @@ -451,4 +446,9 @@ if (backToTop) {
sortRows();
}
updateSortIndicators();
// Filtering collapses rows, so open a linked entry after restoring URL state.
if (location.hash) {
const linkedRow = document.getElementById(decodeURIComponent(location.hash.slice(1)));
if (linkedRow && linkedRow.matches("tr.row")) openEntryRow(linkedRow);
}
})();
63 changes: 63 additions & 0 deletions website/tests/test_entry_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
import shutil
import subprocess
from pathlib import Path

import pytest

NODE = shutil.which("node")
SCRIPT = Path(__file__).resolve().parents[1] / "static/main.js"
RUNNER = r"""
const fs = require("node:fs");
const vm = require("node:vm");
const { script, query } = JSON.parse(fs.readFileSync(0, "utf8"));
const classes = new Set();
const attributes = {"aria-expanded": "false"};
const search = {value: "", addEventListener() {}};
const row = {
hidden: false, textContent: "httpx HTTP client", cells: [{textContent: "1"}],
nextElementSibling: null,
classList: {
contains: name => name === "row" || classes.has(name),
toggle(name, on) { if (on) classes.add(name); else classes.delete(name); }
},
matches: selector => selector === "tr.row",
setAttribute: (name, value) => attributes[name] = value,
querySelector: () => ({textContent: "httpx"})
};
const tbody = {
rows: [row], addEventListener() {}, appendChild() {},
querySelectorAll: selector => selector === "tr.row.open" ? (classes.has("open") ? [row] : []) : [row]
};
const table = {dataset: {defaultSort: "editorial"}, classList: {toggle() {}}};
const document = {
querySelector: selector => ({".search": search, ".table": table, ".table tbody": tbody})[selector] || null,
querySelectorAll: selector => selector === ".table tbody tr.row" ? [row] : [],
getElementById: id => id === "entry-httpx" ? row : null,
createDocumentFragment: () => ({appendChild() {}}),
addEventListener() {}
};
const location = new URL("https://awesome-python.com/categories/http-clients/" + query + "#entry-httpx");
vm.runInNewContext(script, {
document, location,
window: {location, matchMedia: () => ({matches: false})}, URLSearchParams
});
process.stdout.write(JSON.stringify({open: attributes["aria-expanded"], hidden: row.hidden, search: search.value}));
"""


@pytest.mark.parametrize(
("query", "search"),
[("", ""), ("?sort=name&order=asc", ""), ("?q=httpx", "httpx"), ("?q=requests", "")],
)
def test_entry_anchor_opens_after_initial_search_and_sort(query, search):
if NODE is None:
pytest.skip("Entry link script tests require Node.js")
result = subprocess.run(
[NODE, "-e", RUNNER],
input=json.dumps({"script": SCRIPT.read_text(encoding="utf-8"), "query": query}),
capture_output=True,
text=True,
check=True,
)
assert json.loads(result.stdout) == {"open": "true", "hidden": False, "search": search}
Loading