Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35c0d83819 |
@@ -1,12 +1,6 @@
|
|||||||
import { test, expect } from '../support/fixtures.js';
|
import { test, expect } from '../support/fixtures.js';
|
||||||
import type { Page } from '@playwright/test';
|
import type { Page } from '@playwright/test';
|
||||||
|
|
||||||
/**
|
|
||||||
* How far the scroll test scrolls. One constant, because the guard and
|
|
||||||
* the assertion have to agree about it — they did not, which is #133.
|
|
||||||
*/
|
|
||||||
const SCROLL_TARGET = 80;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plan 007 phase 5: expanding an album shows its tracks.
|
* Plan 007 phase 5: expanding an album shows its tracks.
|
||||||
*
|
*
|
||||||
@@ -110,27 +104,20 @@ test.describe('the album dropdown', () => {
|
|||||||
await app.setViewportSize({ width: 900, height: 600 });
|
await app.setViewportSize({ width: 900, height: 600 });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Wait for the range the assertion below actually needs, not for
|
await expect.poll(() => scrollRange(app)).toMatchObject({
|
||||||
// "scrollable at all" (#133). The guard used to be
|
scrollable: true,
|
||||||
// `scrollHeight > clientHeight + 40` while the next line asks to
|
overflowY: 'auto',
|
||||||
// reach 80, so any range in 41-79 satisfied it and could not
|
});
|
||||||
// satisfy the assertion — and the grid passes through exactly
|
|
||||||
// that while it settles, because it recomputes its columns after
|
|
||||||
// the resize rather than during it. The settled range here is
|
|
||||||
// 330, so this waits rather than weakening anything.
|
|
||||||
await expect
|
|
||||||
.poll(() => scrollRange(app))
|
|
||||||
.toMatchObject({ room: true, overflowY: 'auto' });
|
|
||||||
|
|
||||||
await app.evaluate((target) => {
|
await app.evaluate(() => {
|
||||||
const sc = document
|
const sc = document
|
||||||
.querySelector('cover-grid')
|
.querySelector('cover-grid')
|
||||||
?.shadowRoot?.querySelector('.grid-scroll-container');
|
?.shadowRoot?.querySelector('.grid-scroll-container');
|
||||||
|
|
||||||
if (sc) sc.scrollTop = target;
|
if (sc) sc.scrollTop = 80;
|
||||||
}, SCROLL_TARGET);
|
});
|
||||||
|
|
||||||
expect(await scrollTop(app)).toBe(SCROLL_TARGET);
|
expect(await scrollTop(app)).toBe(80);
|
||||||
|
|
||||||
// And the dropdown it opens is on screen, wherever the manager
|
// And the dropdown it opens is on screen, wherever the manager
|
||||||
// decides that leaves the scroll. It is *not* "the position is
|
// decides that leaves the scroll. It is *not* "the position is
|
||||||
@@ -263,19 +250,16 @@ async function closeDropdown(app: Page): Promise<void> {
|
|||||||
|
|
||||||
/** Whether the grid can scroll at all, which decides if a probe can move. */
|
/** Whether the grid can scroll at all, which decides if a probe can move. */
|
||||||
async function scrollRange(app: Page) {
|
async function scrollRange(app: Page) {
|
||||||
return app.evaluate((target) => {
|
return app.evaluate(() => {
|
||||||
const sc = document
|
const sc = document
|
||||||
.querySelector('cover-grid')
|
.querySelector('cover-grid')
|
||||||
?.shadowRoot?.querySelector('.grid-scroll-container');
|
?.shadowRoot?.querySelector('.grid-scroll-container');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// `room` is the precondition of the assertion that follows it:
|
scrollable: !!sc && sc.scrollHeight > sc.clientHeight + 40,
|
||||||
// enough range to actually reach the target. A threshold below
|
|
||||||
// what the caller depends on is not a guard.
|
|
||||||
room: !!sc && sc.scrollHeight - sc.clientHeight >= target,
|
|
||||||
overflowY: sc ? getComputedStyle(sc).overflowY : '',
|
overflowY: sc ? getComputedStyle(sc).overflowY : '',
|
||||||
};
|
};
|
||||||
}, SCROLL_TARGET);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function scrollTop(app: Page): Promise<number> {
|
async function scrollTop(app: Page): Promise<number> {
|
||||||
|
|||||||
+11
-6
@@ -20,14 +20,19 @@ pre-commit:
|
|||||||
glob: "*.go"
|
glob: "*.go"
|
||||||
run: go tool golangci-lint run --timeout 5m ./...
|
run: go tool golangci-lint run --timeout 5m ./...
|
||||||
|
|
||||||
# Snapshots the tree either side of the generators and reports only
|
|
||||||
# what moved across them. This used to be `go generate` plus a bare
|
|
||||||
# `git diff --name-only`, which is the *whole unstaged worktree* — so
|
|
||||||
# any unrelated edit sitting there was reported as stale generated
|
|
||||||
# code, and `make generate` then fixed nothing. See the script.
|
|
||||||
codegen-check:
|
codegen-check:
|
||||||
glob: "*.{go,sql,templ}"
|
glob: "*.{go,sql,templ}"
|
||||||
run: ./scripts/codegen-check.sh
|
run: |
|
||||||
|
go generate ./...
|
||||||
|
if [ -n "$(git diff --name-only)" ]; then
|
||||||
|
echo "Generated code is out of date. Run 'make generate' and stage the changes."
|
||||||
|
# --no-pager, or this blocks forever on `less` waiting for a
|
||||||
|
# keypress that a hook run without a tty will never get: the
|
||||||
|
# commit hangs at exactly the moment it is trying to tell you
|
||||||
|
# why it failed.
|
||||||
|
git --no-pager diff --stat
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# frontend/bindings is generated by `wails3`, not `go generate`, so
|
# frontend/bindings is generated by `wails3`, not `go generate`, so
|
||||||
# the check above does not cover it. ~3.5s warm, ~20s on a cold
|
# the check above does not cover it. ~3.5s warm, ~20s on a cold
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Fails when `go generate ./...` would change something that is not staged.
|
|
||||||
#
|
|
||||||
# The obvious spelling of this is `go generate && git diff --name-only`,
|
|
||||||
# which is what the hook used to be, and it answers the wrong question:
|
|
||||||
# that diff is the *whole unstaged worktree*, so any unrelated edit — a
|
|
||||||
# note, a plan document, the next commit's files sitting there while this
|
|
||||||
# one lands — was reported as
|
|
||||||
#
|
|
||||||
# Generated code is out of date. Run 'make generate' and stage the changes.
|
|
||||||
#
|
|
||||||
# Running `make generate` then does nothing, because nothing generated is
|
|
||||||
# stale, and the message sends you looking for a codegen problem that does
|
|
||||||
# not exist. Splitting one piece of work into several commits is exactly
|
|
||||||
# the shape that triggers it, so the workaround was a constraint on commit
|
|
||||||
# order for no real reason.
|
|
||||||
#
|
|
||||||
# So the tree is snapshotted either side of the generators and only what
|
|
||||||
# *moved across them* is reported. That is deliberately not a list of
|
|
||||||
# generated paths: sqlcgen, `*_templ.go` and `frontend/src/events.ts` are
|
|
||||||
# today's answer, a fourth generator is one `//go:generate` line away, and
|
|
||||||
# a path list is a second place to remember it — the same reasoning that
|
|
||||||
# keeps staleshape.go parsing sql/schemas/ rather than restating it.
|
|
||||||
#
|
|
||||||
# Content, not names: a generated file that is *already* dirty and is then
|
|
||||||
# rewritten further keeps its name in both snapshots and would otherwise
|
|
||||||
# slip through.
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
# name + worktree blob hash for every file that differs from the index.
|
|
||||||
# A file listed but absent (a deletion) hashes as "gone" rather than
|
|
||||||
# aborting the pipeline.
|
|
||||||
snapshot() {
|
|
||||||
git diff --name-only | while IFS= read -r f; do
|
|
||||||
if [ -f "$f" ]; then
|
|
||||||
printf '%s %s\n' "$f" "$(git hash-object -- "$f")"
|
|
||||||
else
|
|
||||||
printf '%s gone\n' "$f"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# A brand-new generated file is not in either diff, because it is not
|
|
||||||
# tracked at all — the same blind spot bindings-check.sh names. Both
|
|
||||||
# snapshots are taken before the generators run.
|
|
||||||
before="$(snapshot)"
|
|
||||||
before_untracked="$(git ls-files --others --exclude-standard)"
|
|
||||||
|
|
||||||
go generate ./...
|
|
||||||
|
|
||||||
after="$(snapshot)"
|
|
||||||
after_untracked="$(git ls-files --others --exclude-standard)"
|
|
||||||
|
|
||||||
# Symmetric difference, and the symmetry is the whole point. Generation
|
|
||||||
# can push a file *into* the unstaged set (it was current, now it is not)
|
|
||||||
# or *out* of it (someone hand-edited generated output and the generator
|
|
||||||
# put it back) — and the second is stale generated code just as much as
|
|
||||||
# the first. Comparing one direction only reports "current" for it,
|
|
||||||
# which is the failure this script was written to stop.
|
|
||||||
moved="$(comm -3 <(printf '%s\n' "$before" | sort) <(printf '%s\n' "$after" | sort) |
|
|
||||||
cut -d' ' -f1 | tr -d '\t' | sort -u | grep -v '^$' || true)"
|
|
||||||
|
|
||||||
if [ -n "$moved" ]; then
|
|
||||||
echo "codegen-check: generated code is out of date." >&2
|
|
||||||
echo "Run 'make generate' and stage:" >&2
|
|
||||||
printf ' %s\n' $moved >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$after_untracked" != "$before_untracked" ]; then
|
|
||||||
echo "codegen-check: generation produced new files. Stage them:" >&2
|
|
||||||
comm -13 <(printf '%s\n' "$before_untracked" | sort) \
|
|
||||||
<(printf '%s\n' "$after_untracked" | sort) >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "codegen-check: generated code is current"
|
|
||||||
Reference in New Issue
Block a user