aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_files_sorting.py
blob: 730d1e8490f064b274a131c6adf37b4c9c6441d2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
"""
Sorting the Files listing (`files-app.js`).

Reported: reversing the name sort, or sorting by size, did nothing — above all
with folders. Folders were `[...dirs].sort()` whatever the column, so only the
files below them moved, and a folder full of folders did not move at all.

Both halves now go through one `sortRows`, run here out of the source.
"""

import json
import re
import shutil
import subprocess
from pathlib import Path

import pytest

STATIC = Path(__file__).resolve().parents[1] / "src" / "meshbay_hub" / "static"
FILES_APP = STATIC / "files-app.js"

pytestmark = pytest.mark.skipif(
    shutil.which("node") is None or not FILES_APP.exists(),
    reason="node or the SPA sources are not available")


@pytest.fixture(scope="module")
def source():
    m = re.search(r"^function sortRows\(.*?^\}", FILES_APP.read_text(encoding="utf-8"),
                  re.M | re.S)
    assert m, "sortRows is no longer where this test reads it from"
    return m.group(0)


def _names(tmp_path, source, rows, key, asc):
    script = tmp_path / "case.js"
    script.write_text(
        f"{source}\nconsole.log(JSON.stringify("
        f"sortRows({json.dumps(rows)}, {json.dumps(key)}, {json.dumps(asc)}).map((r) => r.name)));",
        encoding="utf-8")
    out = subprocess.run(["node", str(script)], capture_output=True, text=True, check=True)
    return json.loads(out.stdout)


FOLDERS = [
    {"name": "Beta", "size": 10, "date": 300},
    {"name": "alpha", "size": 500, "date": 100},
    {"name": "Gamma", "size": 0, "date": 0},
    {"name": "delta", "size": 500, "date": 200},
]


def test_name_both_ways(tmp_path, source):
    up = _names(tmp_path, source, FOLDERS, "name", True)
    assert up == ["alpha", "Beta", "delta", "Gamma"]
    assert _names(tmp_path, source, FOLDERS, "name", False) == up[::-1]


def test_size_both_ways_with_ties_broken_by_name(tmp_path, source):
    assert _names(tmp_path, source, FOLDERS, "size", True) == ["Gamma", "Beta", "alpha", "delta"]
    assert _names(tmp_path, source, FOLDERS, "size", False) == ["alpha", "delta", "Beta", "Gamma"]


def test_date(tmp_path, source):
    assert _names(tmp_path, source, FOLDERS, "date", False) == ["Beta", "delta", "alpha", "Gamma"]


def test_folders_have_no_type_and_fall_back_to_their_names(tmp_path, source):
    assert _names(tmp_path, source, FOLDERS, "type", False) == ["alpha", "Beta", "delta", "Gamma"]


def test_a_missing_size_sorts_as_zero_rather_than_breaking_the_order(tmp_path, source):
    rows = [{"name": "b", "size": 5}, {"name": "a"}, {"name": "c", "size": 1}]
    assert _names(tmp_path, source, rows, "size", True) == ["a", "c", "b"]


def test_folders_and_files_share_one_ordering():
    """Both lists in the listing are ordered by the same function and column."""
    text = FILES_APP.read_text(encoding="utf-8")
    # Code lines only: the comment above sortRows quotes the old line on purpose.
    code = "\n".join(line for line in text.splitlines() if not line.lstrip().startswith("//"))
    assert "[...dirs].sort()" not in code
    assert re.search(r"const sorted = sortRows\(", text)
    assert re.search(r"const subdirs = sortRows\(", text)