HTMX and Playwright: Button press does nothing - playwright

I have a very simple htmx example which works manually, but fails via Playwright:
You can test it here: https://thomas-guettler.de/htmx-snippets/load-fragment-via-hx-get.html
import os
from playwright.sync_api import sync_playwright
def run(playwright):
browser = playwright.chromium.launch(headless=False, devtools=True)
context = browser.new_context()
page = context.new_page()
page.goto("https://thomas-guettler.de/htmx-snippets/load-fragment-via-hx-get.html")
page.click("text=load second button")
page.click("text=Reload Parent OOB")
page.dblclick("text=This should be the end")
# ---------------------
context.close()
browser.close()
os.environ['DEBUG'] = 'pw:api'
with sync_playwright() as playwright:
run(playwright)```
Loading the second button works, but "Reload Parent OOB" fails.
What could be the reason?
2021-03-19T06:39:17.114Z pw:api navigating to "https://thomas-guettler.de/htmx-snippets/load-fragment-via-hx-get.html", waiting until "load"
2021-03-19T06:39:17.317Z pw:api navigated to "https://thomas-guettler.de/htmx-snippets/load-fragment-via-hx-get.html"
2021-03-19T06:39:17.534Z pw:api "domcontentloaded" event fired
2021-03-19T06:39:17.547Z pw:api "load" event fired
2021-03-19T06:39:17.554Z pw:api waiting for selector "text=load second button"
2021-03-19T06:39:17.591Z pw:api selector resolved to visible <button hx-swap="outerHTML" hx-get="second-button.htmx">load second button</button>
2021-03-19T06:39:17.599Z pw:api attempting click action
2021-03-19T06:39:17.599Z pw:api waiting for element to be visible, enabled and stable
2021-03-19T06:39:17.635Z pw:api element is visible, enabled and stable
2021-03-19T06:39:17.635Z pw:api scrolling into view if needed
2021-03-19T06:39:17.636Z pw:api done scrolling
2021-03-19T06:39:17.638Z pw:api checking that element receives pointer events at (117.19,18.48)
2021-03-19T06:39:17.643Z pw:api element does receive pointer events
2021-03-19T06:39:17.643Z pw:api performing click action
2021-03-19T06:39:17.653Z pw:api click action done
2021-03-19T06:39:17.653Z pw:api waiting for scheduled navigations to finish
2021-03-19T06:39:17.656Z pw:api navigations have finished
2021-03-19T06:39:17.660Z pw:api waiting for selector "text=Reload Parent OOB"
2021-03-19T06:39:17.688Z pw:api selector resolved to visible <button class="htmx-settling" hx-get="reload-parent-oob.…>Reload Parent OOB</button>
2021-03-19T06:39:17.692Z pw:api attempting click action
2021-03-19T06:39:17.692Z pw:api waiting for element to be visible, enabled and stable
2021-03-19T06:39:17.716Z pw:api element is visible, enabled and stable
2021-03-19T06:39:17.717Z pw:api scrolling into view if needed
2021-03-19T06:39:17.717Z pw:api done scrolling
2021-03-19T06:39:17.719Z pw:api checking that element receives pointer events at (120.14,18.48)
2021-03-19T06:39:17.727Z pw:api element does receive pointer events
2021-03-19T06:39:17.727Z pw:api performing click action
2021-03-19T06:39:17.730Z pw:api click action done
2021-03-19T06:39:17.730Z pw:api waiting for scheduled navigations to finish
2021-03-19T06:39:17.743Z pw:api navigations have finished
2021-03-19T06:39:17.746Z pw:api waiting for selector "text=This should be the end"
2021-03-19T06:39:18.179Z pw:api "networkidle" event fired
Traceback (most recent call last):
File "/home/guettli/projects/lala-env/src/lala/lala/playwright/test.py", line 24, in <module>
run(playwright)
File "/home/guettli/projects/lala-env/src/lala/lala/playwright/test.py", line 16, in run
page.dblclick("text=This should be the end")
File "/home/guettli/projects/lala-env/lib/python3.8/site-packages/playwright/sync_api/_generated.py", line 6437, in dblclick
self._sync(
File "/home/guettli/projects/lala-env/lib/python3.8/site-packages/playwright/_impl/_sync_base.py", line 103, in _sync
return task.result()
File "/home/guettli/projects/lala-env/lib/python3.8/site-packages/playwright/_impl/_page.py", line 611, in dblclick
return await self._main_frame.dblclick(**locals_to_params(locals()))
File "/home/guettli/projects/lala-env/lib/python3.8/site-packages/playwright/_impl/_frame.py", line 387, in dblclick
await self._channel.send("dblclick", locals_to_params(locals()))
File "/home/guettli/projects/lala-env/lib/python3.8/site-packages/playwright/_impl/_connection.py", line 36, in send
return await self.inner_send(method, params, False)
File "/home/guettli/projects/lala-env/lib/python3.8/site-packages/playwright/_impl/_connection.py", line 47, in inner_send
result = await callback.future
playwright._impl._api_types.TimeoutError: Timeout 30000ms exceeded.
=========================== logs ===========================
waiting for selector "text=This should be the end"

I found a "solution", but I don't like it:
import os
import time
from playwright.sync_api import sync_playwright
def run(playwright):
browser = playwright.chromium.launch(headless=False, devtools=True)
context = browser.new_context()
page = context.new_page()
page.goto("https://thomas-guettler.de/htmx-snippets/load-fragment-via-hx-get.html")
page.click("text=load second button")
time.sleep(0.1) # <<<< ===================== If I wait here, it works ===
page.click("text=Reload Parent OOB")
page.dblclick("text=This should be the end")
# ---------------------
context.close()
browser.close()
os.environ['DEBUG'] = 'pw:api'
with sync_playwright() as playwright:
run(playwright)
I would like to avoid this waiting here.
Is this a bug in Playwright or htmx?

This sounds like an issue with htmx, adding delay to the second click helps:
page.click("text=Reload Parent OOB", delay=100)
Perhaps htmx does something between mouse down and mouse up so that the delay matters. Dario opened an issue in Playwright on this.

Related

Esper EPL - Differentiate deleted events from released events in a timed window

I count events within a timed window. If more than 5 events arrive at that window, then I want to discard them all. Otherwise, the events are released after the waiting time.
My code goes something like this:
// Create a timed window of 10 seconds
create window MyWindow.win:time(10 sec) as MyEventType;
// Add to the timed window
insert into MyWindow select * from MyEventType;
//Delete from window if upper limit was reached
On MyEventType as newEvent
select and delete * from MyWindow as oldEvent
having COUNT(*) >= 5;
Additionally, a listener receives all events that leave the timed window:
select rstream * from MyWindow;
The problem with the example above is that both deleted and released events are forwarded to the listener (via rstream).
Question: how to differentiate deleted events from released ones?
There is nothing on the events afaik. I think an application could look at the time of the event and see if its older than current time. I can think of another option whereas a listener of on-delete receives the deleted events and not the expired ones which given the application a means to know what was deleted and what was expired.

serviceWorker.ready never resolves unless within setTimeout

In a page's domready event I'm setting an event listener for a button that sets a Background Sync event in a page on my website (when offline), but even if the service worker has been loaded already on previous page visits, this line of code never resolves the promise, its status stays "Pending" indefinitely.
navigator.serviceWorker.ready.then(function (sw) {
$('#elt').on('click', function () {
sw.sync.register('contact-form-submission');
Since I know the sync event won't need to be set until after a form is completed, I tried wrapping this code in a setTimeout() of 3 seconds, which worked! My question is why this might be, I haven't been able to find anyone else indicating the .ready event wouldn't be available when the page is first loaded. Insight appreciated.

Cancel long-running TFDBatchMove while in progress

How can I cancel a batch move after calling TFDBatchMove.Execute, while it is still in progress? E.g. if the user presses a Cancel button on the form. I don't see any kind of Cancel method on TFDBatchMove.
I've tried using
raise Exception.Create('Aborted');
in the OnProgress event of the TFDBatchMove (checking a Cancelled flag set by the Cancel button), but after pressing Ok on the exception message window, the batch move continues.
Another possibility is to just close the Reader component, or set Reader := nil in mid flight, but this seems like a bit of a hack.
Call the AbortJob method. It's described like:
Use the AbortJob method to stop the current data movement.
After a call to AbortJob, the method stops reading data from the
reader data source, stops writing data to the writer and terminates
Execute method.

wxLua - How do I implement a Cancel button?

I have a wxLua Gui app that has a "Run" button. Depending on selected options, Run can take a long time, so I would like to implement a "Cancel" button/feature. But it looks like everything in wxLua is working on one Gui thread, and once you hit Run, pressing Cancel does nothing, the Run always goes to completion.
Cancel basically sets a variable to true, and the running process regularly checks that variable. But the Cancel button press event never happens while Running.
I have never used co-routines; if the Run process regularly yields to a "Cancel check" process, will the Cancel event happen then?
Or is there another way?
(the following assumes that by "Run" you mean a long running operation in the same process and not running an external process using wxExecute or wxProcess.)
"Cancel" event is not triggered because by executing your Run logic you have not given a chance to the UI to handle the click event.
To avoid blocking the UI you need to do something like this. When you click Run button create a co-routine around the function you want to run:
coro = coroutine.create(myLongRunningFunction)
Your Run event is completed at this point. Then in EVT_IDLE event you will be resuming this coroutine as long as it's not complete. It will look something like this:
if coro then -- only if there is a coroutine to work on
local ok, res = coroutine.resume(coro, additional, parameters)
-- your function either yielded or returned
-- you may check ok to see if there was an error
-- res can tell you how far you are in the process
-- coro can return multiple values (just give them as parameters to yield)
if coroutine.status(coro) == 'dead' then -- finished or stopped with error
coro = nil
-- do whatever you need to do knowing the process is completed
end
end
You will probably need to request more IDLE event for as long as your process is not finished as some operating systems will not trigger IDLE events unless there is some other event triggered. Assuming your handler has event parameter, you can do event:RequestMore(true) to ask for more IDLE events (RequestMore).
Your long-running process will need to call coroutine.yield() at the right time (not too short as you will be wasting time to switch back and forth and not too long for users to notice delays in the UI); you probably need to experiment with this, but something timer-based with 100ms or so between calls may work.
You can check for Cancel values either in your IDLE event handler or in the long-running function as you do now. The logic I described will give your application UI a chance to process Cancel event as you expect.
I don't use WXWidgets, but the way I implement cancel buttons in my lua scripts which use IUP is to have a cancel flag, which is set when the button is pressed and the progress display is checked for during the run.
Usage is like this
ProgressDisplay.Start('This is my progress box',100)
for i=1,100 do
ProgressDisplay.SetMessage(i.." %")
fhSleep(50,40) -- Emulate performing the task
ProgressDisplay.Step(1)
if ProgressDisplay.Cancel() then
break
end
end
ProgressDisplay.Reset()
ProgressDisplay.Close()
If you want to see the definition for the ProgressDisplay see:
http://www.fhug.org.uk/wiki/doku.php?id=plugins:code_snippets:progress_bar

When will the initial connectionStateChange be fired?

From the docs:
connectionStateChange.addListener
This event will also be fired once during app startup, as soon as we determine the connection status.
I'm having an issue where, in some cases, the listener is bound too late and misses the initial firing of the connection state change event.
At what specific point in time can I start expecting the event to be fired? At what point should I start listening in order to guarantee that I don't miss it?
If you put your binding code outside of any callbacks, you should always be bound to the event before the initial trigger:
// OK
forge.event.connectionStateChange.addListener(function () { ... });
$(function () {
// not necessarily OK
forge.event.connectionStateChange.addListener(function () { ... });
});
I've created a story for us to fire late-bound listeners immediately too, to obviate the issue.

Resources