Compare commits
1
Commits
next
...
329f265e30
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
329f265e30 |
@@ -251,8 +251,9 @@ func TestHandleSourceDetail_RendersNamedTargetFields(
|
|||||||
// page's maximum width at 108rem (1728 px), half again the 72rem of
|
// page's maximum width at 108rem (1728 px), half again the 72rem of
|
||||||
// max-w-6xl that the webhook list and the event log use, so an
|
// max-w-6xl that the webhook list and the event log use, so an
|
||||||
// entrypoint URL fits on one line in a 1920-pixel window; and the
|
// entrypoint URL fits on one line in a 1920-pixel window; and the
|
||||||
// wrapping of its title row, so the buttons beside the title do not
|
// wrapping of its title row and of long values, so neither the buttons
|
||||||
// push a phone-width window into scrolling sideways.
|
// beside the title nor a long name push a phone-width window into
|
||||||
|
// scrolling sideways.
|
||||||
func TestHandleSourceDetail_FitsWideAndNarrowWindows(t *testing.T) {
|
func TestHandleSourceDetail_FitsWideAndNarrowWindows(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -273,7 +274,7 @@ func TestHandleSourceDetail_FitsWideAndNarrowWindows(t *testing.T) {
|
|||||||
|
|
||||||
assert.Contains(
|
assert.Contains(
|
||||||
t, body,
|
t, body,
|
||||||
`<div class="mx-auto px-6 py-8" style="max-width: 108rem"`,
|
`<div class="mx-auto px-6 py-8 wrap-anywhere" style="max-width: 108rem"`,
|
||||||
)
|
)
|
||||||
assert.Contains(
|
assert.Contains(
|
||||||
t, body,
|
t, body,
|
||||||
|
|||||||
@@ -98,20 +98,25 @@ func TestAlpineRunsUnderTheSecurityPolicy(t *testing.T) {
|
|||||||
checkRefusedNewWebhook(ctx, t, srv.URL+"/hooks/new")
|
checkRefusedNewWebhook(ctx, t, srv.URL+"/hooks/new")
|
||||||
checkEventLog(ctx, t, page+"/events", event.ID, older.ID, target.Name)
|
checkEventLog(ctx, t, page+"/events", event.ID, older.ID, target.Name)
|
||||||
checkMobileMenu(ctx, t, page)
|
checkMobileMenu(ctx, t, page)
|
||||||
|
checkPhoneWidth(ctx, t, page, page+"/events", target.Name)
|
||||||
|
|
||||||
assert.Empty(t, problems(), "the browser reported problems")
|
assert.Empty(t, problems(), "the browser reported problems")
|
||||||
}
|
}
|
||||||
|
|
||||||
// seedBrowserWebhook seeds the webhook the browser test loads, owned by
|
// seedBrowserWebhook seeds the webhook the browser test loads, owned by
|
||||||
// userID: an entrypoint, two events, and a target whose delivery of the
|
// userID: an entrypoint, two events, and a target whose delivery of the
|
||||||
// newer event failed once with a 502. It returns the webhook, the older
|
// newer event failed once with a 502. The webhook's name and the newer
|
||||||
// and the newer event, and the target.
|
// event's content type are each too long for one line on a phone. It
|
||||||
|
// returns the webhook, the older and the newer event, and the target.
|
||||||
func seedBrowserWebhook(
|
func seedBrowserWebhook(
|
||||||
t *testing.T, env *testEnv, userID string,
|
t *testing.T, env *testEnv, userID string,
|
||||||
) (*database.Webhook, *database.Event, *database.Event, *database.Target) {
|
) (*database.Webhook, *database.Event, *database.Event, *database.Target) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
webhook := env.seedWebhook(t, userID)
|
webhook := env.seedWebhook(t, userID)
|
||||||
|
require.NoError(t, env.db.DB().Model(webhook).Update(
|
||||||
|
"name", "payment_provider_production_notifications",
|
||||||
|
).Error)
|
||||||
require.NoError(t, env.db.DB().Omit(clause.Associations).Create(
|
require.NoError(t, env.db.DB().Omit(clause.Associations).Create(
|
||||||
&database.Entrypoint{
|
&database.Entrypoint{
|
||||||
WebhookID: webhook.ID,
|
WebhookID: webhook.ID,
|
||||||
@@ -126,6 +131,9 @@ func seedBrowserWebhook(
|
|||||||
|
|
||||||
webhookDB, err := env.dbMgr.GetDB(webhook.ID)
|
webhookDB, err := env.dbMgr.GetDB(webhook.ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, webhookDB.Model(event).Update(
|
||||||
|
"content_type", "application/vnd.paymentprovider.event+json",
|
||||||
|
).Error)
|
||||||
require.NoError(t, webhookDB.Omit(clause.Associations).Create(
|
require.NoError(t, webhookDB.Omit(clause.Associations).Create(
|
||||||
&database.DeliveryResult{
|
&database.DeliveryResult{
|
||||||
DeliveryID: dlv.ID,
|
DeliveryID: dlv.ID,
|
||||||
@@ -1292,3 +1300,60 @@ func checkMobileMenu(ctx context.Context, t *testing.T, url string) {
|
|||||||
click(ctx, t, button)
|
click(ctx, t, button)
|
||||||
assert.True(t, hidden(ctx, menu), "the menu button does not close the menu")
|
assert.True(t, hidden(ctx, menu), "the menu button does not close the menu")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scrollsSideways reports whether the page is wider than the window. A
|
||||||
|
// page's clientWidth is the window's width less its scroll bar.
|
||||||
|
const scrollsSideways = `document.documentElement.scrollWidth >
|
||||||
|
document.documentElement.clientWidth`
|
||||||
|
|
||||||
|
// cutOffElements lists each element, without elements inside it, that
|
||||||
|
// is shown but runs past the page's edge or its card's, by more than a
|
||||||
|
// pixel of rounding. A card hides what runs past its edge.
|
||||||
|
const cutOffElements = `[...document.querySelectorAll("body *")]
|
||||||
|
.filter((el) => {
|
||||||
|
const box = el.getBoundingClientRect();
|
||||||
|
const card = el.closest(".card")?.getBoundingClientRect();
|
||||||
|
const left = card ? card.left : 0;
|
||||||
|
const right = card ? card.right : document.documentElement.clientWidth;
|
||||||
|
return el.children.length === 0 && box.width > 0 &&
|
||||||
|
(box.left < left - 1 || box.right > right + 1);
|
||||||
|
})
|
||||||
|
.map((el) => el.outerHTML.slice(0, 120))`
|
||||||
|
|
||||||
|
// checkPhoneWidth loads the webhook page, url, and its event log,
|
||||||
|
// eventLog, in a phone-sized window, the event log with the attempts of
|
||||||
|
// the newest event's delivery to targetName shown. It checks that
|
||||||
|
// neither page scrolls sideways and that nothing shown on either, no
|
||||||
|
// status, time or control, is cut off at the page's or its card's edge.
|
||||||
|
func checkPhoneWidth(
|
||||||
|
ctx context.Context, t *testing.T, url, eventLog, targetName string,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var (
|
||||||
|
sideways bool
|
||||||
|
cutOff []string
|
||||||
|
)
|
||||||
|
|
||||||
|
measure := chromedp.Tasks{
|
||||||
|
chromedp.Evaluate(scrollsSideways, &sideways),
|
||||||
|
chromedp.Evaluate(cutOffElements, &cutOff),
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, chromedp.Run(
|
||||||
|
ctx,
|
||||||
|
chromedp.EmulateViewport(phoneWidth, phoneHeight),
|
||||||
|
loadPage(url),
|
||||||
|
measure,
|
||||||
|
))
|
||||||
|
assert.False(t, sideways, "the webhook page scrolls sideways on a phone")
|
||||||
|
assert.Empty(t, cutOff, "the webhook page cuts these off on a phone")
|
||||||
|
|
||||||
|
require.NoError(t, chromedp.Run(ctx, loadPage(eventLog)))
|
||||||
|
click(ctx, t, `//span[text()="`+targetName+`"]`)
|
||||||
|
require.True(t, shown(ctx, `//span[text()="Attempt 1"]`),
|
||||||
|
"clicking the delivery does not show its attempts")
|
||||||
|
require.NoError(t, chromedp.Run(ctx, measure))
|
||||||
|
assert.False(t, sideways, "the event log scrolls sideways on a phone")
|
||||||
|
assert.Empty(t, cutOff, "the event log cuts these off on a phone")
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -6,8 +6,10 @@
|
|||||||
<!-- 108rem, half again the 72rem (max-w-6xl) of the webhook list, the
|
<!-- 108rem, half again the 72rem (max-w-6xl) of the webhook list, the
|
||||||
event log, the navbar and the footer, so an entrypoint URL fits on
|
event log, the navbar and the footer, so an entrypoint URL fits on
|
||||||
one line. An inline style, because the committed tailwind.css has
|
one line. An inline style, because the committed tailwind.css has
|
||||||
no class this wide. -->
|
no class this wide. wrap-anywhere: a value with no place to break,
|
||||||
<div class="mx-auto px-6 py-8" style="max-width: 108rem">
|
such as a long name, breaks where it must instead of running past
|
||||||
|
the edge of a phone's screen. -->
|
||||||
|
<div class="mx-auto px-6 py-8 wrap-anywhere" style="max-width: 108rem">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<a href="/hooks" class="btn-small">← Back to webhooks</a>
|
<a href="/hooks" class="btn-small">← Back to webhooks</a>
|
||||||
<div class="flex flex-wrap justify-between items-center gap-2 mt-2">
|
<div class="flex flex-wrap justify-between items-center gap-2 mt-2">
|
||||||
|
|||||||
@@ -3,10 +3,13 @@
|
|||||||
{{define "title"}}Full Event Log - {{.Webhook.Name}} - Webhooker{{end}}
|
{{define "title"}}Full Event Log - {{.Webhook.Name}} - Webhooker{{end}}
|
||||||
|
|
||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="max-w-6xl mx-auto px-6 py-8">
|
<!-- wrap-anywhere: a value with no place to break, such as an event's
|
||||||
|
ID or content type, breaks where it must instead of running past
|
||||||
|
the edge of a phone's screen. -->
|
||||||
|
<div class="max-w-6xl mx-auto px-6 py-8 wrap-anywhere">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<a href="/hook/{{.Webhook.ID}}" class="btn-small">← Back to {{.Webhook.Name}}</a>
|
<a href="/hook/{{.Webhook.ID}}" class="btn-small">← Back to {{.Webhook.Name}}</a>
|
||||||
<div class="flex justify-between items-center mt-2">
|
<div class="flex flex-wrap justify-between items-center gap-2 mt-2">
|
||||||
<h1 class="text-2xl font-medium text-gray-900">Full Event Log</h1>
|
<h1 class="text-2xl font-medium text-gray-900">Full Event Log</h1>
|
||||||
<!-- Under a filter, this counts the events the filter lists. -->
|
<!-- Under a filter, this counts the events the filter lists. -->
|
||||||
<span class="text-sm text-gray-500">{{if gt .TotalEvents (len .Events)}}{{len .Events}} most recent of {{.TotalEvents}} events{{else}}{{.TotalEvents}}{{if not .Show}} total{{end}} event{{if ne .TotalEvents 1}}s{{end}}{{end}}{{if eq .Show "failed"}} with a failed delivery{{else if eq .Show "pending"}} with a delivery pending or retrying{{end}}</span>
|
<span class="text-sm text-gray-500">{{if gt .TotalEvents (len .Events)}}{{len .Events}} most recent of {{.TotalEvents}} events{{else}}{{.TotalEvents}}{{if not .Show}} total{{end}} event{{if ne .TotalEvents 1}}s{{end}}{{end}}{{if eq .Show "failed"}} with a failed delivery{{else if eq .Show "pending"}} with a delivery pending or retrying{{end}}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user