Event log: a double- or triple-click on an event's ID leaves it as it was
check / check (push) Waiting to run
check / check (push) Waiting to run
The first click of a double- or triple-click comes before its selection and toggled the event. A later click of it that finds text selected now puts the event back as it was before the first click. The browser test checks this with a triple-click on the ID, checks the caret and the row's aria-expanded both while the event is expanded and while it is collapsed, and reaches the row with Tab from the page's Back link before pressing Enter and Space. The event log's checks are split in three to stay within the length limit. Model: opus-5-5
This commit is contained in:
@@ -125,6 +125,8 @@ func TestAlpineRunsUnderTheSecurityPolicy(t *testing.T) {
|
||||
checkEntrypointEdit(ctx, t, page, page+"/events")
|
||||
checkRecentEvents(ctx, t, page)
|
||||
checkEventLog(ctx, t, page+"/events", event.ID, target.Name)
|
||||
checkEventSelection(ctx, t, page+"/events", event.ID)
|
||||
checkEventKeyboard(ctx, t, page+"/events", event.ID)
|
||||
checkMobileMenu(ctx, t, page)
|
||||
|
||||
assert.Empty(t, problems(), "the browser reported problems")
|
||||
@@ -610,21 +612,23 @@ func checkRecentEvents(ctx context.Context, t *testing.T, url string) {
|
||||
// checkEventLog loads the event log and checks an event's row. Clicking
|
||||
// its ID expands the event, and in there clicking its delivery shows the
|
||||
// delivery's attempts and clicking again hides them. Clicking the row's
|
||||
// caret collapses the event. Selecting the ID with the mouse leaves the
|
||||
// event collapsed, and the caret's next click expands it. Clicking the
|
||||
// ID again collapses it. From the keyboard, Enter on the row expands
|
||||
// the event and Space collapses it.
|
||||
// caret collapses the event, clicking it again expands it, and clicking
|
||||
// the ID again collapses it. While the event is expanded the row says so
|
||||
// and its caret is turned up, and while it is collapsed neither.
|
||||
func checkEventLog(
|
||||
ctx context.Context, t *testing.T, url, eventID, targetName string,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
// The event's row shows its ID and ends with its caret, and its
|
||||
// Resubmit form is in the part that expands. The delivery's row
|
||||
// there shows the target's name.
|
||||
// The event's row shows its ID and ends with its caret, which turns
|
||||
// up with Tailwind's rotate-180 class, and its Resubmit form is in
|
||||
// the part that expands. The delivery's row there shows the target's
|
||||
// name.
|
||||
id := `//span[text()="` + eventID + `"]`
|
||||
row := id + `/ancestor::div[@role="button"]`
|
||||
caret := row + `//*[local-name()="svg"]`
|
||||
caretUp := caret + `[contains(@class, "rotate-180")]`
|
||||
caretDown := caret + `[not(contains(@class, "rotate-180"))]`
|
||||
expanded := `form[action$="/` + eventID + `/resubmit"]`
|
||||
deliveryRow := `//span[text()="` + targetName + `"]`
|
||||
attempt := `//span[text()="Attempt 1"]`
|
||||
@@ -636,6 +640,10 @@ func checkEventLog(
|
||||
click(ctx, t, id)
|
||||
assert.True(t, shown(ctx, expanded),
|
||||
"clicking the event's ID does not expand it")
|
||||
assert.True(t, shown(ctx, row+`[@aria-expanded="true"]`),
|
||||
"the expanded event's row does not say it is expanded")
|
||||
assert.True(t, shown(ctx, caretUp),
|
||||
"the expanded event's caret does not turn up")
|
||||
|
||||
assert.True(t, hidden(ctx, attempt), "the delivery's attempts start shown")
|
||||
|
||||
@@ -650,12 +658,41 @@ func checkEventLog(
|
||||
click(ctx, t, caret)
|
||||
assert.True(t, hidden(ctx, expanded),
|
||||
"clicking the caret does not collapse the event")
|
||||
assert.True(t, shown(ctx, row+`[@aria-expanded="false"]`),
|
||||
"the collapsed event's row does not say it is collapsed")
|
||||
assert.True(t, shown(ctx, caretDown),
|
||||
"the collapsed event's caret stays turned up")
|
||||
|
||||
click(ctx, t, caret)
|
||||
assert.True(t, shown(ctx, expanded),
|
||||
"clicking the caret again does not expand the event")
|
||||
|
||||
click(ctx, t, id)
|
||||
assert.True(t, hidden(ctx, expanded),
|
||||
"clicking the event's ID again does not collapse it")
|
||||
}
|
||||
|
||||
// checkEventSelection loads the event log and checks that selecting an
|
||||
// event's ID with the mouse leaves the event as it was: dragging over the
|
||||
// ID leaves it collapsed, the caret's next click still expands it, and a
|
||||
// triple-click on the ID then leaves it expanded.
|
||||
func checkEventSelection(
|
||||
ctx context.Context, t *testing.T, url, eventID string,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
id := `//span[text()="` + eventID + `"]`
|
||||
row := id + `/ancestor::div[@role="button"]`
|
||||
caret := row + `//*[local-name()="svg"]`
|
||||
expanded := `form[action$="/` + eventID + `/resubmit"]`
|
||||
|
||||
var (
|
||||
selected, state string
|
||||
hasState bool
|
||||
)
|
||||
|
||||
require.NoError(t, chromedp.Run(ctx, loadPage(url)))
|
||||
|
||||
selectText(ctx, t, id)
|
||||
require.NoError(t, chromedp.Run(
|
||||
ctx,
|
||||
@@ -668,19 +705,50 @@ func checkEventLog(
|
||||
require.True(t, hasState, "the event's row does not say if it is expanded")
|
||||
assert.Equal(t, "false", state, "selecting the event's ID expands it")
|
||||
|
||||
// The caret's click also clears the selection, so the triple-click's
|
||||
// first click finds nothing selected, as a person's would.
|
||||
click(ctx, t, caret)
|
||||
assert.True(t, shown(ctx, expanded),
|
||||
"clicking the caret after selecting the ID does not expand the event")
|
||||
|
||||
click(ctx, t, id)
|
||||
assert.True(t, hidden(ctx, expanded),
|
||||
"clicking the event's ID again does not collapse it")
|
||||
tripleClick(ctx, t, id)
|
||||
require.NoError(t, chromedp.Run(
|
||||
ctx,
|
||||
chromedp.Evaluate(`window.getSelection().toString()`, &selected),
|
||||
chromedp.AttributeValue(
|
||||
row, "aria-expanded", &state, &hasState, chromedp.BySearch,
|
||||
),
|
||||
))
|
||||
assert.Contains(t, selected, eventID,
|
||||
"a triple-click does not select the event's ID")
|
||||
assert.Equal(t, "true", state,
|
||||
"a triple-click selecting the event's ID collapses it")
|
||||
}
|
||||
|
||||
// checkEventKeyboard loads the event log and checks that Tab from the
|
||||
// page's Back link reaches the event's row, the first after it, and that
|
||||
// Enter then expands the event and Space collapses it.
|
||||
func checkEventKeyboard(
|
||||
ctx context.Context, t *testing.T, url, eventID string,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
back := `//a[contains(text(), "Back to")]`
|
||||
expanded := `form[action$="/` + eventID + `/resubmit"]`
|
||||
|
||||
var focused string
|
||||
|
||||
require.NoError(t, chromedp.Run(
|
||||
ctx,
|
||||
chromedp.Focus(row, chromedp.BySearch),
|
||||
chromedp.KeyEvent(kb.Enter),
|
||||
loadPage(url),
|
||||
chromedp.Focus(back, chromedp.BySearch),
|
||||
chromedp.KeyEvent(kb.Tab),
|
||||
chromedp.Evaluate(`document.activeElement.textContent`, &focused),
|
||||
))
|
||||
require.Contains(t, focused, eventID,
|
||||
"Tab from the Back link does not reach the event's row")
|
||||
|
||||
require.NoError(t, chromedp.Run(ctx, chromedp.KeyEvent(kb.Enter)))
|
||||
assert.True(t, shown(ctx, expanded), "Enter does not expand the event")
|
||||
|
||||
require.NoError(t, chromedp.Run(ctx, chromedp.KeyEvent(" ")))
|
||||
@@ -714,6 +782,31 @@ func selectText(ctx context.Context, t *testing.T, xpath string) {
|
||||
))
|
||||
}
|
||||
|
||||
// tripleClick clicks three times in a row in the middle of the element
|
||||
// matching an XPath expression, as a person does to select a whole line
|
||||
// of text. The browser selects a word on the second click and the whole
|
||||
// paragraph on the third.
|
||||
func tripleClick(ctx context.Context, t *testing.T, xpath string) {
|
||||
t.Helper()
|
||||
|
||||
var box *dom.BoxModel
|
||||
|
||||
require.NoError(t, chromedp.Run(
|
||||
ctx, chromedp.Dimensions(xpath, &box, chromedp.BySearch),
|
||||
))
|
||||
|
||||
// The content box's corners, clockwise from its top left.
|
||||
x := (box.Content[0] + box.Content[2]) / 2
|
||||
y := (box.Content[1] + box.Content[5]) / 2
|
||||
|
||||
require.NoError(t, chromedp.Run(
|
||||
ctx,
|
||||
chromedp.MouseClickXY(x, y, chromedp.ClickCount(1)),
|
||||
chromedp.MouseClickXY(x, y, chromedp.ClickCount(2)),
|
||||
chromedp.MouseClickXY(x, y, chromedp.ClickCount(3)),
|
||||
))
|
||||
}
|
||||
|
||||
// checkMobileMenu loads a page in a phone-sized window and checks that
|
||||
// the menu button opens and closes the mobile menu.
|
||||
func checkMobileMenu(ctx context.Context, t *testing.T, url string) {
|
||||
|
||||
+18
-4
@@ -76,17 +76,31 @@ document.addEventListener("alpine:init", function () {
|
||||
window.Alpine.data("collapsible", function () {
|
||||
return {
|
||||
open: false,
|
||||
// Whether it was open before its latest single click, which
|
||||
// may be the first of a double- or triple-click.
|
||||
wasOpen: false,
|
||||
init() {
|
||||
this.open = this.$root.hasAttribute("data-open");
|
||||
},
|
||||
toggle() {
|
||||
this.open = !this.open;
|
||||
},
|
||||
// Toggles, except on a click that ends a text selection, such
|
||||
// as selecting an event's ID to copy it.
|
||||
toggleUnlessSelecting() {
|
||||
if (window.getSelection().toString() === "") {
|
||||
// Toggles on a click, except one that selects text, such as
|
||||
// selecting an event's ID to copy it. A double- or
|
||||
// triple-click selects its text on its second click, after
|
||||
// its first may have toggled, so a later click that finds
|
||||
// text selected puts back the state from before the first.
|
||||
toggleUnlessSelecting(event) {
|
||||
const selecting = window.getSelection().toString() !== "";
|
||||
|
||||
if (event.detail === 1) {
|
||||
this.wasOpen = this.open;
|
||||
}
|
||||
|
||||
if (!selecting) {
|
||||
this.toggle();
|
||||
} else if (event.detail > 1) {
|
||||
this.open = this.wasOpen;
|
||||
}
|
||||
},
|
||||
get closed() {
|
||||
|
||||
Reference in New Issue
Block a user