File size: 4,049 Bytes
f4ffc94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"""The updater drives a page it does not own, so pin the two things that are
actually ours: which rows get ticked, and finding a toolbar button across three
Joomla generations. The browser itself is faked — this is about the logic, and
a real run against a live site is a dry run away.
"""
import admin
import mesh
import pytest


class Loc:
    """The slice of Playwright's Locator this module uses."""

    def __init__(self, items=(), text=""):
        self.items = list(items)
        self.text = text
        self.checked = False
        self.clicked = 0

    # a locator is both a set and, via .first/.nth, its members
    def count(self):
        return len(self.items) if self.items else (1 if self.text else 0)

    def all(self):
        return self.items

    def nth(self, i):
        return self.items[i]

    @property
    def first(self):
        return self.items[0] if self.items else self

    def inner_text(self):
        return self.text

    def check(self):
        self.checked = True

    def click(self):
        self.clicked += 1

    def get_attribute(self, _):
        return self.text

    def locator(self, sel):
        return self.sub[sel]


def _row(cid, name):
    cb = Loc(text=cid)
    cells = Loc([Loc(text="")] * 1 + [Loc(text=f"{name}\nhttps://update.example/x.xml")])
    tr = Loc(text=name)
    tr.sub = {"td, th": cells}
    cb.sub = {"xpath=ancestor::tr[1]": tr}
    return cb


class Page:
    def __init__(self, rows=(), buttons=(), alerts=()):
        self.rows = {r.text: r for r in rows}
        self.buttons = dict(buttons)
        self.alerts = list(alerts)
        self.waited = []

    def locator(self, sel):
        if sel == 'input[name="cid[]"]':
            return Loc(list(self.rows.values()))
        if sel.startswith('input[name="cid[]"][value='):
            return Loc([self.rows[sel.split('"')[3]]])
        if sel == "#system-message-container .alert":
            return Loc([Loc(text=a) for a in self.alerts])
        return self.buttons.get(sel, Loc())

    def wait_for_load_state(self, *a, **kw):
        self.waited.append(a)


ROWS = [_row("101", "Regular Labs - Snippets"), _row("102", "YOOtheme - Joomla Template")]
MODERN = 'joomla-toolbar-button[task="update.update"] button'
ANCIENT = 'button[onclick*="update.update"]'


def test_only_the_named_rows_are_ticked():
    page = Page(ROWS, {MODERN: Loc([Loc(text="btn")])}, alerts=["Update completed."])
    picked, messages = admin.apply_updates(page, only=["yootheme"])

    assert [p["label"] for p in picked] == ["YOOtheme - Joomla Template"]
    assert page.rows["102"].checked is True
    assert page.rows["101"].checked is False   # the one we did not ask for
    assert messages == ["Update completed."]


def test_no_filter_means_every_pending_row():
    page = Page(ROWS, {MODERN: Loc([Loc(text="btn")])})
    picked, _ = admin.apply_updates(page)
    assert len(picked) == 2 and all(r.checked for r in page.rows.values())


def test_a_joomla_3_toolbar_is_still_found():
    page = Page(ROWS, {ANCIENT: Loc([Loc(text="btn")])})
    admin.apply_updates(page)
    assert page.buttons[ANCIENT].items[0].clicked == 1


def test_no_update_button_is_an_error_not_a_silent_no_op():
    page = Page(ROWS)
    with pytest.raises(admin.AdminError):
        admin.apply_updates(page)


def test_nothing_pending_never_presses_update():
    page = Page([], {MODERN: Loc([Loc(text="btn")])})
    picked, messages = admin.apply_updates(page)
    assert picked == [] and messages == []
    assert page.buttons[MODERN].items[0].clicked == 0


def test_typed_credentials_beat_the_stored_ones(monkeypatch):
    monkeypatch.setattr(mesh, "env", lambda site=None: {
        "ADMIN_USER": "stored", "ADMIN_PASS": "storedpw"})
    assert admin.credentials("x") == ("stored", "storedpw")
    assert admin.credentials("x", "typed", "typedpw") == ("typed", "typedpw")


def test_sign_in_without_credentials_refuses_before_opening_the_site():
    with pytest.raises(admin.AdminError):
        admin.sign_in(None, "https://x.test", "", "")