diff --git a/db.sqlite b/db.sqlite new file mode 100644 index 0000000..e69de29 diff --git a/python_flask/packager.bundle b/python_flask/packager.bundle new file mode 100644 index 0000000..37ce34d Binary files /dev/null and b/python_flask/packager.bundle differ diff --git a/python_flask/packager/__init__.py b/python_flask/packager/__init__.py index 81b5fc4..a59ec44 100644 --- a/python_flask/packager/__init__.py +++ b/python_flask/packager/__init__.py @@ -7,7 +7,7 @@ from .helpers import * from flask_sqlalchemy import SQLAlchemy 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 db = SQLAlchemy(app) diff --git a/python_flask/packager/components/Home.py b/python_flask/packager/components/Home.py new file mode 100644 index 0000000..bc22b30 --- /dev/null +++ b/python_flask/packager/components/Home.py @@ -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 diff --git a/python_flask/packager/components/NewPackageList.py b/python_flask/packager/components/NewPackageList.py index ef1f73a..660cfac 100644 --- a/python_flask/packager/components/NewPackageList.py +++ b/python_flask/packager/components/NewPackageList.py @@ -17,7 +17,17 @@ def NewPackageList(name=None, description=None, error=False, errormsg=None): target="_self", method="post", _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: with t.div(_class=cls("mb-5", "flex", "flex-row", "items-center")): 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_post="/list/name/validate", data_hx_swap="outerHTML", + data_hx_trigger="changed", _class=cls( "block", "w-full", @@ -92,7 +103,7 @@ def NewPackageList(name=None, description=None, error=False, errormsg=None): type="submit", 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( "py-2", diff --git a/python_flask/packager/components/PackageListManager.py b/python_flask/packager/components/PackageListManager.py index 35a8bd4..a3f4cbf 100644 --- a/python_flask/packager/components/PackageListManager.py +++ b/python_flask/packager/components/PackageListManager.py @@ -12,13 +12,7 @@ class PackageListManager: self, pkglists, name=None, description=None, error=False, errormsg=None ): assert not (error and not errormsg) - with t.div( - id="pkglist-manager", - _class=cls("p-8", "max-w-xl"), - **{ - "x-data": '{ submit_enabled: document.getElementById("listname").value.trim().length !== 0 }' - }, - ) as doc: + with t.div(id="pkglist-manager", _class=cls("p-8", "max-w-xl")) as doc: PackageListTable(pkglists), NewPackageList( name=name, description=description, error=error, errormsg=errormsg diff --git a/python_flask/packager/components/PackageListTable.py b/python_flask/packager/components/PackageListTable.py index 5e4cb41..1bbecbc 100644 --- a/python_flask/packager/components/PackageListTable.py +++ b/python_flask/packager/components/PackageListTable.py @@ -5,7 +5,102 @@ from dominate.util import raw 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): 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")), @@ -14,6 +109,8 @@ class PackageListTableRow: t.span(_class=cls("mdi", "mdi-delete", "text-xl")), id="delete-packagelist", data_hx_delete=f"/list/{pkglist.id}", + data_hx_target="closest tr", + data_hx_swap="outerHTML", _class=cls( "border", "bg-red-200", @@ -23,10 +120,7 @@ class PackageListTableRow: "text-center", ), ), - t.td( - t.span(_class=cls("mdi", "mdi-pencil", "text-xl")), - id="edit-packagelist", - data_hx_post=f"/list/{pkglist.id}/edit", + with t.td( _class=cls( "border", "bg-blue-200", @@ -34,12 +128,20 @@ class PackageListTableRow: "cursor-pointer", "w-8", "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.span(_class=cls("mdi", "mdi-arrow-right", "text-xl")), id="edit-packagelist", - # data_hx_post=f"/list/{pkglist.id}/edit", + # data_hx_post=f"/list/{pkglist.id}/show", _class=cls( "border", "bg-green-200", @@ -52,9 +154,16 @@ class PackageListTableRow: 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): - doc = t.div(id="packagelist-table") - with doc: + with t.div(id="packagelist-table") as doc: t.h1("Package Lists", _class=cls("text-2xl", "mb-5")) with t.table( id="packagelist-table", @@ -76,7 +185,7 @@ def PackageListTable(pkglists): t.th(_class=cls("border p-2")), _class="h-10", ) - with t.tbody(data_hx_target="closest tr", data_hx_swap="outerHTML"): + with t.tbody() as b: for pkglist in pkglists: PackageListTableRow(pkglist).doc diff --git a/python_flask/packager/components/__init__.py b/python_flask/packager/components/__init__.py index 96c8d75..6780327 100644 --- a/python_flask/packager/components/__init__.py +++ b/python_flask/packager/components/__init__.py @@ -1,4 +1,9 @@ from .NewPackageList import NewPackageList -from .PackageListTable import PackageListTable +from .PackageListTable import ( + PackageListTable, + PackageListTableRowEdit, + PackageListTableRowNormal, + PackageListTableRow, +) from .PackageListManager import PackageListManager from .Home import Home diff --git a/python_flask/packager/helpers.py b/python_flask/packager/helpers.py index 319fcbd..5053546 100644 --- a/python_flask/packager/helpers.py +++ b/python_flask/packager/helpers.py @@ -1,2 +1,14 @@ def cls(*args): 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) + "}" diff --git a/python_flask/packager/models.py b/python_flask/packager/models.py index 75becab..94f82e1 100644 --- a/python_flask/packager/models.py +++ b/python_flask/packager/models.py @@ -8,6 +8,10 @@ class PackageList(db.Model): description = db.Column(db.Text) items = db.relationship("PackageListItem", backref="packagelist", lazy=True) + edit = False + error = False + errormsg = None + class PackageListItem(db.Model): __tablename__ = "packagelistitem" diff --git a/python_flask/packager/views.py b/python_flask/packager/views.py index 1f8c630..d48203b 100644 --- a/python_flask/packager/views.py +++ b/python_flask/packager/views.py @@ -10,7 +10,14 @@ import dominate import dominate.tags as t 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 @@ -45,8 +52,29 @@ def delete_packagelist(id): @app.route("/") 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( - 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"]) def add_new_list(): - print(f"headers: {request.headers}") name = request.form["name"] description = request.form["description"] @@ -69,15 +96,13 @@ def add_new_list(): if is_htmx(): return make_response( - str( - PackageListManager( - get_packagelists(), - name=name, - description=description, - error=error, - errormsg=errormsg, - ) - ), + PackageListManager( + get_packagelists(), + name=name, + description=description, + error=error, + errormsg=errormsg, + ).doc.render(), 200 if error else 201, ) else: @@ -116,309 +141,63 @@ def validate_list_name(): @app.route("/list//edit/cancel", methods=["POST"]) def edit_list_cancel(id): + print("cancelling" * 20) pkglist = PackageList.query.filter_by(id=str(id)).first() - - 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) + return make_response(PackageListTableRowNormal(pkglist).doc.render(), 200) -@app.route("/list//edit/submit", methods=["POST"]) +@app.route("/list//edit/submit/", methods=["POST"]) def edit_list_submit(id): name = request.form["name"] description = request.form["description"] - if len(name) == 0: - with t.tr(id="pkglist-edit-row") as doc: - with t.td(colspan=3, _class=cls("border-none", "bg-purple-100", "h-10")): - t.p("Name cannot be empty", _class=cls("text-red-400", "text-sm")) - with t.div(_class=cls("flex", "flex-row", "h-full")): - with t.div( - _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) + error, errormsg = validate_name(name) + + if error: + if is_htmx(): + return make_response( + PackageListTableRowEdit(error=True, errormsg=errormsg).doc.render(), 200 + ) + else: + r = make_response("", 303) + r.headers["Location"] = f"/?edit={id}&error=1&msg={errormsg}" + return r + + pkglist = PackageList.query.filter_by(id=str(id)).first() + if pkglist is None: + # todo what to do without js? + return make_response("", 404) + + pkglist.name = name + pkglist.description = description try: - pkglist = PackageList.query.filter_by(id=str(id)).first() - if pkglist is None: - return make_response("", 404) - pkglist.name = name - pkglist.description = description - try: - db.session.commit() - except sqlalchemy.exc.IntegrityError: - with t.tr(id="pkglist-edit-row") as doc: - with t.td( - colspan=3, _class=cls("border-none", "bg-purple-100", "h-10") - ): - t.p( - f"Name {name} already exists", - _class=cls("text-red-400", "text-sm"), - ) - with t.div(_class=cls("flex", "flex-row", "h-full")): - with t.div( - _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 + db.session.commit() + except sqlalchemy.exc.IntegrityError: + db.session.rollback() + errormsg = f'Name "{name}" already exists' + if is_htmx(): + pkglist.error = True + pkglist.errormsg = errormsg + return make_response(PackageListTableRowEdit(pkglist).doc.render(), 200) + else: + r = make_response("", 303) + r.headers["Location"] = f"/?edit={id}&name={name}&error=1&msg={errormsg}" + return r - 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) - - -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 + if is_htmx(): + return make_response(PackageListTableRowNormal(pkglist).doc.render(), 200) + else: + r = make_response("", 303) + r.headers["Location"] = "/" + return r @app.route("/list//edit", methods=["POST"]) def edit_list(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/", methods=["DELETE"]) diff --git a/python_flask/selenium/.gitignore b/python_flask/selenium/.gitignore new file mode 100644 index 0000000..9f21b54 --- /dev/null +++ b/python_flask/selenium/.gitignore @@ -0,0 +1 @@ +/venv/ diff --git a/python_flask/selenium/connect.sh b/python_flask/selenium/connect.sh new file mode 100755 index 0000000..a59157b --- /dev/null +++ b/python_flask/selenium/connect.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +xtightvncviewer 127.0.0.1::5900 -passwd <(printf %s secret | vncpasswd -f) diff --git a/python_flask/selenium/requirements.txt b/python_flask/selenium/requirements.txt new file mode 100644 index 0000000..6acfc18 --- /dev/null +++ b/python_flask/selenium/requirements.txt @@ -0,0 +1,3 @@ +helium==3.0.8 +selenium==3.141.0 +urllib3==1.26.10 diff --git a/python_flask/selenium/run-driver.sh b/python_flask/selenium/run-driver.sh new file mode 100755 index 0000000..07ced3e --- /dev/null +++ b/python_flask/selenium/run-driver.sh @@ -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 diff --git a/python_flask/selenium/setup.sh b/python_flask/selenium/setup.sh new file mode 100755 index 0000000..8434070 --- /dev/null +++ b/python_flask/selenium/setup.sh @@ -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 diff --git a/python_flask/selenium/test.py b/python_flask/selenium/test.py new file mode 100755 index 0000000..ab21ef5 --- /dev/null +++ b/python_flask/selenium/test.py @@ -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()