Selenium
This commit is contained in:
BIN
python_flask/packager.bundle
Normal file
BIN
python_flask/packager.bundle
Normal file
Binary file not shown.
@@ -7,7 +7,7 @@ from .helpers import *
|
|||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///./db.sqlite"
|
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{app.root_path}/../db.sqlite"
|
||||||
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
||||||
db = SQLAlchemy(app)
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
|
|||||||
22
python_flask/packager/components/Home.py
Normal file
22
python_flask/packager/components/Home.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import dominate
|
||||||
|
import dominate.tags as t
|
||||||
|
from dominate.util import raw
|
||||||
|
|
||||||
|
|
||||||
|
class Home:
|
||||||
|
def __init__(self, element, root_path):
|
||||||
|
doc = dominate.document(title="Packager")
|
||||||
|
with doc.head:
|
||||||
|
t.script(src="https://unpkg.com/htmx.org@1.7.0")
|
||||||
|
t.script(src="https://cdn.tailwindcss.com")
|
||||||
|
t.script(src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.js", defer=True)
|
||||||
|
t.link(
|
||||||
|
rel="stylesheet",
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css",
|
||||||
|
)
|
||||||
|
with doc:
|
||||||
|
t.script(raw(open(os.path.join(root_path, "js/app.js")).read()))
|
||||||
|
doc.add(element.doc)
|
||||||
|
self.doc = doc
|
||||||
@@ -17,7 +17,17 @@ def NewPackageList(name=None, description=None, error=False, errormsg=None):
|
|||||||
target="_self",
|
target="_self",
|
||||||
method="post",
|
method="post",
|
||||||
_class=cls("mt-8", "p-5", "border-2", "border-gray-200"),
|
_class=cls("mt-8", "p-5", "border-2", "border-gray-200"),
|
||||||
**{"x-on:htmx:before-request": "(e) => submit_enabled || e.preventDefault()"},
|
**{
|
||||||
|
"x-on:htmx:before-request": "(e) => submit_enabled || e.preventDefault()",
|
||||||
|
"x-data": alpinedata(
|
||||||
|
{
|
||||||
|
"submit_enabled": (
|
||||||
|
jsbool(not error)
|
||||||
|
+ '&& document.getElementById("listname").value.trim().length !== 0'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
},
|
||||||
) as doc:
|
) as doc:
|
||||||
with t.div(_class=cls("mb-5", "flex", "flex-row", "items-center")):
|
with t.div(_class=cls("mb-5", "flex", "flex-row", "items-center")):
|
||||||
t.span(_class=cls("mdi", "mdi-playlist-plus", "text-2xl", "mr-4"))
|
t.span(_class=cls("mdi", "mdi-playlist-plus", "text-2xl", "mr-4"))
|
||||||
@@ -41,6 +51,7 @@ def NewPackageList(name=None, description=None, error=False, errormsg=None):
|
|||||||
data_hx_target="#new-pkglist",
|
data_hx_target="#new-pkglist",
|
||||||
data_hx_post="/list/name/validate",
|
data_hx_post="/list/name/validate",
|
||||||
data_hx_swap="outerHTML",
|
data_hx_swap="outerHTML",
|
||||||
|
data_hx_trigger="changed",
|
||||||
_class=cls(
|
_class=cls(
|
||||||
"block",
|
"block",
|
||||||
"w-full",
|
"w-full",
|
||||||
@@ -92,7 +103,7 @@ def NewPackageList(name=None, description=None, error=False, errormsg=None):
|
|||||||
type="submit",
|
type="submit",
|
||||||
value="Add",
|
value="Add",
|
||||||
**{
|
**{
|
||||||
"x-bind:class": 'submit_enabled ? "" : "cursor-not-allowed opacity-50"'
|
"x-bind:class": 'submit_enabled ? "cursor-pointer" : "cursor-not-allowed opacity-50"'
|
||||||
},
|
},
|
||||||
_class=cls(
|
_class=cls(
|
||||||
"py-2",
|
"py-2",
|
||||||
|
|||||||
@@ -12,13 +12,7 @@ class PackageListManager:
|
|||||||
self, pkglists, name=None, description=None, error=False, errormsg=None
|
self, pkglists, name=None, description=None, error=False, errormsg=None
|
||||||
):
|
):
|
||||||
assert not (error and not errormsg)
|
assert not (error and not errormsg)
|
||||||
with t.div(
|
with t.div(id="pkglist-manager", _class=cls("p-8", "max-w-xl")) as doc:
|
||||||
id="pkglist-manager",
|
|
||||||
_class=cls("p-8", "max-w-xl"),
|
|
||||||
**{
|
|
||||||
"x-data": '{ submit_enabled: document.getElementById("listname").value.trim().length !== 0 }'
|
|
||||||
},
|
|
||||||
) as doc:
|
|
||||||
PackageListTable(pkglists),
|
PackageListTable(pkglists),
|
||||||
NewPackageList(
|
NewPackageList(
|
||||||
name=name, description=description, error=error, errormsg=errormsg
|
name=name, description=description, error=error, errormsg=errormsg
|
||||||
|
|||||||
@@ -5,7 +5,102 @@ from dominate.util import raw
|
|||||||
from ..helpers import *
|
from ..helpers import *
|
||||||
|
|
||||||
|
|
||||||
class PackageListTableRow:
|
class PackageListTableRowEdit:
|
||||||
|
def __init__(self, pkglist):
|
||||||
|
error, errormsg = pkglist.error, pkglist.errormsg
|
||||||
|
assert not (error and not errormsg)
|
||||||
|
with t.tr(
|
||||||
|
_class=cls("h-10", "even:bg-gray-100", "hover:bg-purple-200"),
|
||||||
|
id="pkglist-edit-row",
|
||||||
|
**{
|
||||||
|
"x-data": '{ edit_submit_enabled: document.getElementById("listedit-name").value.trim().length !== 0 }'
|
||||||
|
},
|
||||||
|
) as doc:
|
||||||
|
with t.td(colspan=3, _class=cls("border-none", "bg-purple-100", "h-10")):
|
||||||
|
with t.div():
|
||||||
|
t.form(
|
||||||
|
id="edit-pkglist",
|
||||||
|
action=f"/list/{pkglist.id}/edit/submit/",
|
||||||
|
target="_self",
|
||||||
|
method="post",
|
||||||
|
data_hx_post=f"/list/{pkglist.id}/edit/submit",
|
||||||
|
data_hx_target="closest tr",
|
||||||
|
data_hx_swap="outerHTML",
|
||||||
|
)
|
||||||
|
if error:
|
||||||
|
t.p(errormsg, _class=cls("text-red-400", "text-sm"))
|
||||||
|
with t.div(_class=cls("flex", "flex-row", "h-full")):
|
||||||
|
with t.div(
|
||||||
|
_class=cls(
|
||||||
|
"border",
|
||||||
|
"border-1",
|
||||||
|
"border-purple-500",
|
||||||
|
"bg-purple-100",
|
||||||
|
"mr-1",
|
||||||
|
)
|
||||||
|
):
|
||||||
|
t._input(
|
||||||
|
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
||||||
|
type="text",
|
||||||
|
id="listedit-name",
|
||||||
|
form="edit-pkglist",
|
||||||
|
name="name",
|
||||||
|
value=pkglist.name if error else pkglist.name,
|
||||||
|
**{
|
||||||
|
"x-on:input": "edit_submit_enabled = $event.srcElement.value.trim().length !== 0;"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
with t.div(
|
||||||
|
_class=cls(
|
||||||
|
"border", "border-1", "border-purple-500", "bg-purple-100"
|
||||||
|
)
|
||||||
|
):
|
||||||
|
t._input(
|
||||||
|
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
||||||
|
type="text",
|
||||||
|
name="description",
|
||||||
|
form="edit-pkglist",
|
||||||
|
value=pkglist.description,
|
||||||
|
)
|
||||||
|
with t.td(
|
||||||
|
_class=cls(
|
||||||
|
"border",
|
||||||
|
"bg-red-200",
|
||||||
|
"hover:bg-red-400",
|
||||||
|
"cursor-pointer",
|
||||||
|
"w-8",
|
||||||
|
"text-center",
|
||||||
|
),
|
||||||
|
id="edit-packagelist-abort",
|
||||||
|
):
|
||||||
|
with t.a(
|
||||||
|
href="/",
|
||||||
|
data_hx_post=f"/list/{pkglist.id}/edit/cancel",
|
||||||
|
data_hx_target="closest tr",
|
||||||
|
data_hx_swap="outerHTML",
|
||||||
|
):
|
||||||
|
t.span(_class=cls("mdi", "mdi-cancel", "text-xl")),
|
||||||
|
with t.td(
|
||||||
|
id="edit-packagelist-save",
|
||||||
|
_class=cls(
|
||||||
|
"border",
|
||||||
|
"bg-green-200",
|
||||||
|
"hover:bg-green-400",
|
||||||
|
"cursor-pointer",
|
||||||
|
"w-8",
|
||||||
|
"text-center",
|
||||||
|
),
|
||||||
|
**{
|
||||||
|
"x-bind:class": 'edit_submit_enabled || "cursor-not-allowed opacity-50"',
|
||||||
|
"x-on:htmx:before-request": "(e) => edit_submit_enabled || e.preventDefault()",
|
||||||
|
},
|
||||||
|
):
|
||||||
|
with t.button(type="submit", form="edit-pkglist"):
|
||||||
|
t.span(_class=cls("mdi", "mdi-content-save", "text-xl")),
|
||||||
|
self.doc = doc
|
||||||
|
|
||||||
|
|
||||||
|
class PackageListTableRowNormal:
|
||||||
def __init__(self, pkglist):
|
def __init__(self, pkglist):
|
||||||
with t.tr(_class=cls("h-10", "even:bg-gray-100", "hover:bg-purple-200")) as doc:
|
with t.tr(_class=cls("h-10", "even:bg-gray-100", "hover:bg-purple-200")) as doc:
|
||||||
t.td(pkglist.name, _class=cls("border", "px-2")),
|
t.td(pkglist.name, _class=cls("border", "px-2")),
|
||||||
@@ -14,6 +109,8 @@ class PackageListTableRow:
|
|||||||
t.span(_class=cls("mdi", "mdi-delete", "text-xl")),
|
t.span(_class=cls("mdi", "mdi-delete", "text-xl")),
|
||||||
id="delete-packagelist",
|
id="delete-packagelist",
|
||||||
data_hx_delete=f"/list/{pkglist.id}",
|
data_hx_delete=f"/list/{pkglist.id}",
|
||||||
|
data_hx_target="closest tr",
|
||||||
|
data_hx_swap="outerHTML",
|
||||||
_class=cls(
|
_class=cls(
|
||||||
"border",
|
"border",
|
||||||
"bg-red-200",
|
"bg-red-200",
|
||||||
@@ -23,10 +120,7 @@ class PackageListTableRow:
|
|||||||
"text-center",
|
"text-center",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
t.td(
|
with t.td(
|
||||||
t.span(_class=cls("mdi", "mdi-pencil", "text-xl")),
|
|
||||||
id="edit-packagelist",
|
|
||||||
data_hx_post=f"/list/{pkglist.id}/edit",
|
|
||||||
_class=cls(
|
_class=cls(
|
||||||
"border",
|
"border",
|
||||||
"bg-blue-200",
|
"bg-blue-200",
|
||||||
@@ -34,12 +128,20 @@ class PackageListTableRow:
|
|||||||
"cursor-pointer",
|
"cursor-pointer",
|
||||||
"w-8",
|
"w-8",
|
||||||
"text-center",
|
"text-center",
|
||||||
),
|
)
|
||||||
),
|
):
|
||||||
|
with t.a(
|
||||||
|
id="edit-packagelist",
|
||||||
|
data_hx_post=f"/list/{pkglist.id}/edit",
|
||||||
|
href=f"/?edit={pkglist.id}",
|
||||||
|
data_hx_target="closest tr",
|
||||||
|
data_hx_swap="outerHTML",
|
||||||
|
):
|
||||||
|
t.span(_class=cls("mdi", "mdi-pencil", "text-xl")),
|
||||||
t.td(
|
t.td(
|
||||||
t.span(_class=cls("mdi", "mdi-arrow-right", "text-xl")),
|
t.span(_class=cls("mdi", "mdi-arrow-right", "text-xl")),
|
||||||
id="edit-packagelist",
|
id="edit-packagelist",
|
||||||
# data_hx_post=f"/list/{pkglist.id}/edit",
|
# data_hx_post=f"/list/{pkglist.id}/show",
|
||||||
_class=cls(
|
_class=cls(
|
||||||
"border",
|
"border",
|
||||||
"bg-green-200",
|
"bg-green-200",
|
||||||
@@ -52,9 +154,16 @@ class PackageListTableRow:
|
|||||||
self.doc = doc
|
self.doc = doc
|
||||||
|
|
||||||
|
|
||||||
|
class PackageListTableRow:
|
||||||
|
def __init__(self, pkglist):
|
||||||
|
if pkglist.edit:
|
||||||
|
self.doc = PackageListTableRowEdit(pkglist).doc
|
||||||
|
else:
|
||||||
|
self.doc = PackageListTableRowNormal(pkglist).doc
|
||||||
|
|
||||||
|
|
||||||
def PackageListTable(pkglists):
|
def PackageListTable(pkglists):
|
||||||
doc = t.div(id="packagelist-table")
|
with t.div(id="packagelist-table") as doc:
|
||||||
with doc:
|
|
||||||
t.h1("Package Lists", _class=cls("text-2xl", "mb-5"))
|
t.h1("Package Lists", _class=cls("text-2xl", "mb-5"))
|
||||||
with t.table(
|
with t.table(
|
||||||
id="packagelist-table",
|
id="packagelist-table",
|
||||||
@@ -76,7 +185,7 @@ def PackageListTable(pkglists):
|
|||||||
t.th(_class=cls("border p-2")),
|
t.th(_class=cls("border p-2")),
|
||||||
_class="h-10",
|
_class="h-10",
|
||||||
)
|
)
|
||||||
with t.tbody(data_hx_target="closest tr", data_hx_swap="outerHTML"):
|
with t.tbody() as b:
|
||||||
for pkglist in pkglists:
|
for pkglist in pkglists:
|
||||||
PackageListTableRow(pkglist).doc
|
PackageListTableRow(pkglist).doc
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
from .NewPackageList import NewPackageList
|
from .NewPackageList import NewPackageList
|
||||||
from .PackageListTable import PackageListTable
|
from .PackageListTable import (
|
||||||
|
PackageListTable,
|
||||||
|
PackageListTableRowEdit,
|
||||||
|
PackageListTableRowNormal,
|
||||||
|
PackageListTableRow,
|
||||||
|
)
|
||||||
from .PackageListManager import PackageListManager
|
from .PackageListManager import PackageListManager
|
||||||
from .Home import Home
|
from .Home import Home
|
||||||
|
|||||||
@@ -1,2 +1,14 @@
|
|||||||
def cls(*args):
|
def cls(*args):
|
||||||
return " ".join([a for a in args if a is not None])
|
return " ".join([a for a in args if a is not None])
|
||||||
|
|
||||||
|
|
||||||
|
def jsbool(b):
|
||||||
|
return "true" if b else "false"
|
||||||
|
|
||||||
|
|
||||||
|
def alpinedata(d):
|
||||||
|
elements = []
|
||||||
|
for k, v in d.items():
|
||||||
|
elements.append(f"{k}: " + v)
|
||||||
|
|
||||||
|
return "{" + ",".join(elements) + "}"
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ class PackageList(db.Model):
|
|||||||
description = db.Column(db.Text)
|
description = db.Column(db.Text)
|
||||||
items = db.relationship("PackageListItem", backref="packagelist", lazy=True)
|
items = db.relationship("PackageListItem", backref="packagelist", lazy=True)
|
||||||
|
|
||||||
|
edit = False
|
||||||
|
error = False
|
||||||
|
errormsg = None
|
||||||
|
|
||||||
|
|
||||||
class PackageListItem(db.Model):
|
class PackageListItem(db.Model):
|
||||||
__tablename__ = "packagelistitem"
|
__tablename__ = "packagelistitem"
|
||||||
|
|||||||
@@ -10,7 +10,14 @@ import dominate
|
|||||||
import dominate.tags as t
|
import dominate.tags as t
|
||||||
from dominate.util import raw
|
from dominate.util import raw
|
||||||
|
|
||||||
from .components import PackageListManager, NewPackageList, Home
|
from .components import (
|
||||||
|
PackageListManager,
|
||||||
|
NewPackageList,
|
||||||
|
Home,
|
||||||
|
PackageListTableRowEdit,
|
||||||
|
PackageListTableRowNormal,
|
||||||
|
PackageListTableRow,
|
||||||
|
)
|
||||||
|
|
||||||
from flask import request, make_response
|
from flask import request, make_response
|
||||||
|
|
||||||
@@ -45,8 +52,29 @@ def delete_packagelist(id):
|
|||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def root():
|
def root():
|
||||||
|
packagelists = get_packagelists()
|
||||||
|
error = False
|
||||||
|
if not is_htmx():
|
||||||
|
edit = request.args.get("edit")
|
||||||
|
if edit is not None:
|
||||||
|
match = [p for p in packagelists if p.id == edit]
|
||||||
|
if match:
|
||||||
|
match[0].edit = True
|
||||||
|
error = request.args.get("error")
|
||||||
|
if error and bool(int(error)):
|
||||||
|
match[0].error = True
|
||||||
|
errormsg = request.args.get("msg")
|
||||||
|
if errormsg:
|
||||||
|
match[0].errormsg = errormsg
|
||||||
|
else:
|
||||||
|
name = request.args.get("name")
|
||||||
|
if name:
|
||||||
|
match[0].errormsg = f"Invalid name: {name}"
|
||||||
|
else:
|
||||||
|
match[0].errormsg = f"Invalid name"
|
||||||
|
|
||||||
return make_response(
|
return make_response(
|
||||||
Home(PackageListManager(get_packagelists()), app.root_path).doc.render(), 200
|
Home(PackageListManager(packagelists), app.root_path).doc.render(), 200
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +84,6 @@ def is_htmx():
|
|||||||
|
|
||||||
@app.route("/list/", methods=["POST"])
|
@app.route("/list/", methods=["POST"])
|
||||||
def add_new_list():
|
def add_new_list():
|
||||||
print(f"headers: {request.headers}")
|
|
||||||
name = request.form["name"]
|
name = request.form["name"]
|
||||||
description = request.form["description"]
|
description = request.form["description"]
|
||||||
|
|
||||||
@@ -69,15 +96,13 @@ def add_new_list():
|
|||||||
|
|
||||||
if is_htmx():
|
if is_htmx():
|
||||||
return make_response(
|
return make_response(
|
||||||
str(
|
|
||||||
PackageListManager(
|
PackageListManager(
|
||||||
get_packagelists(),
|
get_packagelists(),
|
||||||
name=name,
|
name=name,
|
||||||
description=description,
|
description=description,
|
||||||
error=error,
|
error=error,
|
||||||
errormsg=errormsg,
|
errormsg=errormsg,
|
||||||
)
|
).doc.render(),
|
||||||
),
|
|
||||||
200 if error else 201,
|
200 if error else 201,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -116,309 +141,63 @@ def validate_list_name():
|
|||||||
|
|
||||||
@app.route("/list/<uuid:id>/edit/cancel", methods=["POST"])
|
@app.route("/list/<uuid:id>/edit/cancel", methods=["POST"])
|
||||||
def edit_list_cancel(id):
|
def edit_list_cancel(id):
|
||||||
|
print("cancelling" * 20)
|
||||||
pkglist = PackageList.query.filter_by(id=str(id)).first()
|
pkglist = PackageList.query.filter_by(id=str(id)).first()
|
||||||
|
return make_response(PackageListTableRowNormal(pkglist).doc.render(), 200)
|
||||||
with t.tr(_class=cls("h-10", "even:bg-gray-100", "hover:bg-purple-200")) as doc:
|
|
||||||
t.td(pkglist.name, _class=cls("border", "px-2")),
|
|
||||||
t.td(str(pkglist.description), _class=cls("border", "px-2")),
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-delete", "text-xl")),
|
|
||||||
id="delete-packagelist",
|
|
||||||
data_hx_delete=f"/list/{pkglist.id}",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-red-200",
|
|
||||||
"hover:bg-red-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-pencil", "text-xl")),
|
|
||||||
id="edit-packagelist",
|
|
||||||
data_hx_post=f"/list/{pkglist.id}/edit",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-blue-200",
|
|
||||||
"hover:bg-blue-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
return make_response(doc.render(), 200)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/list/<uuid:id>/edit/submit", methods=["POST"])
|
@app.route("/list/<uuid:id>/edit/submit/", methods=["POST"])
|
||||||
def edit_list_submit(id):
|
def edit_list_submit(id):
|
||||||
name = request.form["name"]
|
name = request.form["name"]
|
||||||
description = request.form["description"]
|
description = request.form["description"]
|
||||||
if len(name) == 0:
|
error, errormsg = validate_name(name)
|
||||||
with t.tr(id="pkglist-edit-row") as doc:
|
|
||||||
with t.td(colspan=3, _class=cls("border-none", "bg-purple-100", "h-10")):
|
if error:
|
||||||
t.p("Name cannot be empty", _class=cls("text-red-400", "text-sm"))
|
if is_htmx():
|
||||||
with t.div(_class=cls("flex", "flex-row", "h-full")):
|
return make_response(
|
||||||
with t.div(
|
PackageListTableRowEdit(error=True, errormsg=errormsg).doc.render(), 200
|
||||||
_class=cls(
|
)
|
||||||
"box-border" "border",
|
else:
|
||||||
"border-2",
|
r = make_response("", 303)
|
||||||
"border-red-500",
|
r.headers["Location"] = f"/?edit={id}&error=1&msg={errormsg}"
|
||||||
"bg-purple-100",
|
return r
|
||||||
"mr-1",
|
|
||||||
)
|
|
||||||
):
|
|
||||||
with t.div(_class=cls("h-full")):
|
|
||||||
t._input(
|
|
||||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
|
||||||
type="text",
|
|
||||||
name="name",
|
|
||||||
value=name,
|
|
||||||
)
|
|
||||||
with t.div(
|
|
||||||
_class=cls(
|
|
||||||
"border", "border-1", "border-purple-500", "bg-purple-100"
|
|
||||||
)
|
|
||||||
):
|
|
||||||
t._input(
|
|
||||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
|
||||||
type="text",
|
|
||||||
name="description",
|
|
||||||
value=description,
|
|
||||||
)
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-cancel", "text-xl")),
|
|
||||||
id="edit-packagelist-abort",
|
|
||||||
data_hx_post=f"/list/{id}/edit/cancel",
|
|
||||||
data_hx_target="#pkglist-edit-row",
|
|
||||||
data_hx_swap="outerHTML",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-red-200",
|
|
||||||
"hover:bg-red-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-content-save", "text-xl")),
|
|
||||||
id="edit-packagelist-save",
|
|
||||||
data_hx_post=f"/list/{id}/edit/submit",
|
|
||||||
data_hx_target="#pkglist-edit-row",
|
|
||||||
data_hx_swap="outerHTML",
|
|
||||||
data_hx_include="closest tr",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-green-200",
|
|
||||||
"hover:bg-green-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
return make_response(doc.render(), 200)
|
|
||||||
|
|
||||||
try:
|
|
||||||
pkglist = PackageList.query.filter_by(id=str(id)).first()
|
pkglist = PackageList.query.filter_by(id=str(id)).first()
|
||||||
if pkglist is None:
|
if pkglist is None:
|
||||||
|
# todo what to do without js?
|
||||||
return make_response("", 404)
|
return make_response("", 404)
|
||||||
|
|
||||||
pkglist.name = name
|
pkglist.name = name
|
||||||
pkglist.description = description
|
pkglist.description = description
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except sqlalchemy.exc.IntegrityError:
|
except sqlalchemy.exc.IntegrityError:
|
||||||
with t.tr(id="pkglist-edit-row") as doc:
|
db.session.rollback()
|
||||||
with t.td(
|
errormsg = f'Name "{name}" already exists'
|
||||||
colspan=3, _class=cls("border-none", "bg-purple-100", "h-10")
|
if is_htmx():
|
||||||
):
|
pkglist.error = True
|
||||||
t.p(
|
pkglist.errormsg = errormsg
|
||||||
f"Name {name} already exists",
|
return make_response(PackageListTableRowEdit(pkglist).doc.render(), 200)
|
||||||
_class=cls("text-red-400", "text-sm"),
|
else:
|
||||||
)
|
r = make_response("", 303)
|
||||||
with t.div(_class=cls("flex", "flex-row", "h-full")):
|
r.headers["Location"] = f"/?edit={id}&name={name}&error=1&msg={errormsg}"
|
||||||
with t.div(
|
return r
|
||||||
_class=cls(
|
|
||||||
"box-border" "border",
|
|
||||||
"border-2",
|
|
||||||
"border-red-500",
|
|
||||||
"bg-purple-100",
|
|
||||||
"mr-1",
|
|
||||||
)
|
|
||||||
):
|
|
||||||
with t.div(_class=cls("h-full")):
|
|
||||||
t._input(
|
|
||||||
_class=cls(
|
|
||||||
"bg-purple-100", "w-full", "h-full", "px-2"
|
|
||||||
),
|
|
||||||
type="text",
|
|
||||||
name="name",
|
|
||||||
value=name,
|
|
||||||
)
|
|
||||||
with t.div(
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"border-1",
|
|
||||||
"border-purple-500",
|
|
||||||
"bg-purple-100",
|
|
||||||
)
|
|
||||||
):
|
|
||||||
t._input(
|
|
||||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
|
||||||
type="text",
|
|
||||||
name="description",
|
|
||||||
value=description,
|
|
||||||
)
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-cancel", "text-xl")),
|
|
||||||
id="edit-packagelist-abort",
|
|
||||||
data_hx_post=f"/list/{id}/edit/cancel",
|
|
||||||
data_hx_target="#pkglist-edit-row",
|
|
||||||
data_hx_swap="outerHTML",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-red-200",
|
|
||||||
"hover:bg-red-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-content-save", "text-xl")),
|
|
||||||
id="edit-packagelist-save",
|
|
||||||
data_hx_post=f"/list/{id}/edit/submit",
|
|
||||||
data_hx_target="#pkglist-edit-row",
|
|
||||||
data_hx_swap="outerHTML",
|
|
||||||
data_hx_include="closest tr",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-green-200",
|
|
||||||
"hover:bg-green-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
return make_response(doc.render(), 200)
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
|
|
||||||
with t.tr(_class=cls("h-10", "even:bg-gray-100", "hover:bg-purple-200")) as doc:
|
if is_htmx():
|
||||||
t.td(pkglist.name, _class=cls("border", "px-2")),
|
return make_response(PackageListTableRowNormal(pkglist).doc.render(), 200)
|
||||||
t.td(str(pkglist.description), _class=cls("border", "px-2")),
|
else:
|
||||||
t.td(
|
r = make_response("", 303)
|
||||||
t.span(_class=cls("mdi", "mdi-delete", "text-xl")),
|
r.headers["Location"] = "/"
|
||||||
id="delete-packagelist",
|
return r
|
||||||
data_hx_delete=f"/list/{pkglist.id}",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-red-200",
|
|
||||||
"hover:bg-red-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-pencil", "text-xl")),
|
|
||||||
id="edit-packagelist",
|
|
||||||
data_hx_post=f"/list/{pkglist.id}/edit",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-blue-200",
|
|
||||||
"hover:bg-blue-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
return make_response(doc.render(), 200)
|
|
||||||
|
|
||||||
|
|
||||||
def get_edit_list(pkglist):
|
|
||||||
with t.tr(
|
|
||||||
_class="h-10",
|
|
||||||
id="pkglist-edit-row",
|
|
||||||
**{
|
|
||||||
"x-data": '{ edit_submit_enabled: document.getElementById("listedit-name").value.trim().length !== 0 }'
|
|
||||||
},
|
|
||||||
) as doc:
|
|
||||||
with t.td(colspan=3, _class=cls("border-none", "bg-purple-100", "h-full")):
|
|
||||||
with t.div(_class=cls("flex", "flex-row", "h-full")):
|
|
||||||
with t.div(
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"border-1",
|
|
||||||
"border-purple-500",
|
|
||||||
"bg-purple-100",
|
|
||||||
"mr-1",
|
|
||||||
)
|
|
||||||
):
|
|
||||||
t._input(
|
|
||||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
|
||||||
type="text",
|
|
||||||
id="listedit-name",
|
|
||||||
name="name",
|
|
||||||
value=pkglist.name,
|
|
||||||
**{
|
|
||||||
"x-on:input": "edit_submit_enabled = $event.srcElement.value.trim().length !== 0;"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
with t.div(
|
|
||||||
_class=cls(
|
|
||||||
"border", "border-1", "border-purple-500", "bg-purple-100"
|
|
||||||
)
|
|
||||||
):
|
|
||||||
t._input(
|
|
||||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
|
||||||
type="text",
|
|
||||||
name="description",
|
|
||||||
value=pkglist.description,
|
|
||||||
)
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-cancel", "text-xl")),
|
|
||||||
id="edit-packagelist-abort",
|
|
||||||
data_hx_post=f"/list/{pkglist.id}/edit/cancel",
|
|
||||||
data_hx_target="#pkglist-edit-row",
|
|
||||||
data_hx_swap="outerHTML",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-red-200",
|
|
||||||
"hover:bg-red-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
t.td(
|
|
||||||
t.span(_class=cls("mdi", "mdi-content-save", "text-xl")),
|
|
||||||
id="edit-packagelist-save",
|
|
||||||
data_hx_post=f"/list/{pkglist.id}/edit/submit",
|
|
||||||
data_hx_target="#pkglist-edit-row",
|
|
||||||
data_hx_swap="outerHTML",
|
|
||||||
data_hx_include="closest #pkglist-edit-row",
|
|
||||||
_class=cls(
|
|
||||||
"border",
|
|
||||||
"bg-green-200",
|
|
||||||
"hover:bg-green-400",
|
|
||||||
"cursor-pointer",
|
|
||||||
"w-8",
|
|
||||||
"text-center",
|
|
||||||
),
|
|
||||||
**{
|
|
||||||
"x-bind:class": 'edit_submit_enabled || "cursor-not-allowed opacity-50"',
|
|
||||||
"x-on:htmx:before-request": "(e) => edit_submit_enabled || e.preventDefault()",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
return doc
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/list/<uuid:id>/edit", methods=["POST"])
|
@app.route("/list/<uuid:id>/edit", methods=["POST"])
|
||||||
def edit_list(id):
|
def edit_list(id):
|
||||||
pkglist = get_packagelist_by_id(id)
|
pkglist = get_packagelist_by_id(id)
|
||||||
|
|
||||||
return make_response(get_edit_list(pkglist).render(), 200)
|
out = PackageListTableRowEdit(pkglist).doc
|
||||||
|
return make_response(out.render(), 200)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/list/<uuid:id>", methods=["DELETE"])
|
@app.route("/list/<uuid:id>", methods=["DELETE"])
|
||||||
|
|||||||
1
python_flask/selenium/.gitignore
vendored
Normal file
1
python_flask/selenium/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/venv/
|
||||||
3
python_flask/selenium/connect.sh
Executable file
3
python_flask/selenium/connect.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
xtightvncviewer 127.0.0.1::5900 -passwd <(printf %s secret | vncpasswd -f)
|
||||||
3
python_flask/selenium/requirements.txt
Normal file
3
python_flask/selenium/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
helium==3.0.8
|
||||||
|
selenium==3.141.0
|
||||||
|
urllib3==1.26.10
|
||||||
10
python_flask/selenium/run-driver.sh
Executable file
10
python_flask/selenium/run-driver.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
docker run \
|
||||||
|
--rm \
|
||||||
|
--publish 4444:4444 \
|
||||||
|
--env SE_OPTS="--session-timeout 36000" \
|
||||||
|
--shm-size="2g" \
|
||||||
|
--net=host \
|
||||||
|
--name docker-selenium \
|
||||||
|
selenium/standalone-firefox:4.3.0-20220706
|
||||||
11
python_flask/selenium/setup.sh
Executable file
11
python_flask/selenium/setup.sh
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
set -p pipefail
|
||||||
|
|
||||||
|
python3 -m venv ./venv
|
||||||
|
source ./venv/bin/activate
|
||||||
|
python3 -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
sudo apt install tigervnc-common xtightvncviewer
|
||||||
66
python_flask/selenium/test.py
Executable file
66
python_flask/selenium/test.py
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from helium import *
|
||||||
|
import selenium
|
||||||
|
|
||||||
|
from selenium.webdriver.common.keys import Keys
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
|
||||||
|
opts = selenium.webdriver.firefox.options.Options()
|
||||||
|
|
||||||
|
profile = selenium.webdriver.FirefoxProfile()
|
||||||
|
profile.set_preference("javascript.enabled", "false")
|
||||||
|
profile.DEFAULT_PREFERENCES['frozen']['javascript.enabled'] = False
|
||||||
|
opts.profile = profile
|
||||||
|
|
||||||
|
driver = selenium.webdriver.Remote(
|
||||||
|
command_executor="http://localhost:4444/wd/hub",
|
||||||
|
options=opts,
|
||||||
|
)
|
||||||
|
driver.implicitly_wait(0)
|
||||||
|
Config.implicit_wait_secs = 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
helium.set_driver(driver)
|
||||||
|
helium.go_to("http://localhost:5000")
|
||||||
|
|
||||||
|
assert driver.title == "Packager"
|
||||||
|
|
||||||
|
new_entry = Text("Add new package list")
|
||||||
|
|
||||||
|
lists_before = find_all(S("table > tbody > tr", below=Text("Package Lists")))
|
||||||
|
|
||||||
|
write("newlist", into=TextField(to_right_of="Name"))
|
||||||
|
write("newlistdesc", into=TextField(to_right_of="Description"))
|
||||||
|
click(Button("Add"))
|
||||||
|
|
||||||
|
lists_after = find_all(S("table > tbody > tr", below=Text("Package Lists")))
|
||||||
|
|
||||||
|
assert len(lists_before) == len(lists_after) - 1
|
||||||
|
|
||||||
|
nameidx = next(i for i,v in enumerate(find_all(S("table > thead > tr > th"))) if v.web_element.text == "Name")
|
||||||
|
descidx = next(i for i,v in enumerate(find_all(S("table > thead > tr > th"))) if v.web_element.text == "Description")
|
||||||
|
|
||||||
|
new_entry = lists_after[-1]
|
||||||
|
|
||||||
|
cells = new_entry.web_element.find_elements_by_tag_name("td")
|
||||||
|
|
||||||
|
assert cells[nameidx].text == "newlist"
|
||||||
|
assert cells[descidx].text == "newlistdesc"
|
||||||
|
|
||||||
|
lists_before = lists_after
|
||||||
|
|
||||||
|
deletebtn = new_entry.web_element.find_element_by_class_name("mdi-delete")
|
||||||
|
|
||||||
|
click(deletebtn)
|
||||||
|
|
||||||
|
lists_after = find_all(S("table > tbody > tr", below=Text("Package Lists")))
|
||||||
|
|
||||||
|
assert len(lists_before) - 1 == len(lists_after)
|
||||||
|
|
||||||
|
import code; code.interact(local=locals())
|
||||||
|
time.sleep(5)
|
||||||
|
finally:
|
||||||
|
driver.quit()
|
||||||
Reference in New Issue
Block a user