Add prestashop-pr-qa guide an AI agent through QA of a pull request - #4
Conversation
bf3a1ba to
f4c0c44
Compare
f4c0c44 to
a547b5a
Compare
a547b5a to
0e4681a
Compare
|
Thanks for this. The two-phase design with the state canary is a real addition to how we QA PRs today, and splitting long knowledge into Two concrete bugs1. The run-directory guard never runs. In 2. The flake re-sample is inert in the scenario template. const shown = (await page.locator('...').first().innerText()).trim();
await assert.bug('...', async () => shown === '...', `observed "${shown}"`);The closure returns the same Related: when the re-sample flips, Packaging:
|
|
Tested on PrestaShop/PrestaShop#42229 (working on a new feature) and PrestaShop/PrestaShop#41414, looks really cool ! |
Co-authored-by: Thomas N <Quetzacoalt91@users.noreply.github.com>
…ills_fork into feat/prestashop-pr-qa
|
Re-reviewed at Summary
Only 1 is worth fixing before this ships, since it shows up in every report produced. 2 is next: it fails silently green. First, what I checked and found genuinely fixed:
1. The responsive net is broken in every browser report
ok: status > 0 && status < 400 && !fatal && m.rendered && m.over === 0,
Rendered from a synthetic run where the narrow page is perfectly healthy (status 200, rendered, So every run ships two to six unexplained yellow warnings in the section a reviewer looks at to decide whether the PR broke the narrow layouts. Fix is Worth a second look at the same spot: 2. A back-office surface can pass green with no session
const loggedIn = BO ? (await page.locator('input[name="passwd"]').count()) === 0 : false;That does not test "the scenario logged in", it tests "the page currently loaded is not a login form". After a front-office scenario the current page is a front-office page, so The report then says 3. Smaller ones
4. Process, for a later iterationNot reading or setting labels is a deliberate choice and it is stated in the skill, so that one is settled. Three gaps against how we actually QA remain:
5. One scope questionThe PR reflows the whole README (32 lines rewritten that have nothing to do with the skill) and adds "one paragraph per line, no hard wrapping" to the contribution guide. The rule is reasonable and the reflow follows from it, but it is a repository-wide convention arriving inside a skill PR. Worth the maintainers agreeing to it explicitly rather than inheriting it. |
mattgoud
left a comment
There was a problem hiding this comment.
Requesting changes for finding 1 of the review above: scripts/run.js:379 reads m.rendered, which does not exist on the object page.evaluate returns, so ok is undefined on every responsive row and the report badges every narrow page "pre-existing" with no reason given. It is a one-word fix, but it is in the section a reviewer reads to decide whether the PR broke the narrow layouts, so it affects every report the skill produces.
Finding 2 (run.js:297, a back-office surface recorded green when only the login form was seen) is the other one I would not ship, since it fails silently in the direction of approving.
Everything else in that comment is a follow-up, and the rest of the rewrite is a clear step up.
| viewport: name, size: `${VIEWS[name].width}x${VIEWS[name].height}`, url: target, | ||
| status, responds: status > 0 && status < 400 && !fatal, rendered, | ||
| overflowPx: m.over, worst: m.worst, shot, | ||
| ok: status > 0 && status < 400 && !fatal && m.rendered && m.over === 0, |
There was a problem hiding this comment.
Is not rendered instead ? Before m returns a Page instance and there's not rendered attribute
| * No absolute paths, **no run-directory paths**, no credentials. The reader cannot see your disk. A screenshot is referred to by what it shows, never by its filename. | ||
| * At most about 15 lines. The detail stays in `report.md`. | ||
| * `index.md` is the file nobody pastes, so everything the person doing the pasting needs goes there: each comment file with its target URL, one line on why it is being posted, and **the list of screenshots and videos to attach by hand**, because images and video cannot be uploaded programmatically. One line long for a single target, which is fine: it is how they know they are done. | ||
|
|
There was a problem hiding this comment.
Maybe precise to check if sensitive data is not inside screenshot and video before uploading it.
| const USAGE = 'usage: --phase=before|after [--cwd=<working directory>] [--scenario=./scenario.js] [--out=.]'; | ||
| const { phase: PHASE, scenarioPath: SCENARIO, out: OUT, arg } = commonArgs(USAGE); | ||
| const CWD = path.resolve(arg('cwd', process.cwd())); | ||
| const TIMEOUT = Number(arg('timeout', 120)) * 1000; // two minutes: a cache:clear on a cold environment is slow |
There was a problem hiding this comment.
Number('foo') will return NaN. Use parseInt instead
There was a problem hiding this comment.
parseInt('foo') returns NaN as well, so the swap does not change the outcome:
Number("foo") = NaN | parseInt("foo") = NaN
Number("120abc") = NaN | parseInt("120abc") = 120
It would actually loosen the check: parseInt silently accepts 120abc where Number rejects it, and a truncated timeout is worse than a rejected one.
There is a real defect behind it, though. A bad --timeout reaches execFile, which throws before running anything:
RangeError [ERR_OUT_OF_RANGE]: The value of "timeout" is out of range. It must be an unsigned integer. Received NaN
sh() runs inside step(), so that throw is caught and recorded as a harness error: the phase exits 2 with an obscure message on the first command instead of a usage line at startup. --phase already gets that treatment in commonArgs, and --timeout deserves the same:
const TIMEOUT = Number(arg('timeout', 120)) * 1000;
if (!Number.isFinite(TIMEOUT) || TIMEOUT <= 0) { console.error(USAGE); process.exit(2); }--timeout is also missing from the USAGE string, so nothing tells the caller it exists.
| ### 3. Write scenario.js from the ticket's steps | ||
|
|
||
| Write it from the **ticket's** steps first. Read the diff only afterwards, and only to find which page to open and whether a build is needed. One `step()` per test step. The template, the assertion kinds and the runner's API are in [references/runner.md](references/runner.md). | ||
|
|
||
| **In a browser run**, call `clip()` once, in the step where the symptom is visible, naming a container that exists in both phases. The report leads with that clipped pair, which is what makes it readable. A command-line or HTTP run has nothing to clip: its evidence is the paired transcript. See [references/runner.md](references/runner.md). | ||
|
|
||
| If the ticket is about mobile, declare `viewport: 'mobile'` in the scenario, so the bug is measured at the width where it was reported. Browser runs only. See [references/runner.md](references/runner.md). | ||
|
|
||
| Then derive `surfaces`, the pages this PR touches on both sides of the shop, and propose the list before writing it into the scenario. On a back-office PR this is what catches the breakage the ticket never thought to mention. See [references/runner.md](references/runner.md). | ||
|
|
||
| Then check every bug assertion against the tokens the diff adds. The recipe is in [references/runner.md](references/runner.md). A bug assertion naming a class, id or attribute the PR introduces proves nothing: on the code from before the fix that selector is simply absent, the check fails, and the run claims a reproduction it never made. Rewrite it in the words of the ticket. |
There was a problem hiding this comment.
It's a good idea to write a new scenario to test the PR but what about selecting existing tests campaigns stored in tests/UI depending of what PR changes ? I know it's required to run UI Tests for each PR but it could be another guardrail to be sure that there's no regression.
|
Thanks for the reviews. The findings are fixed in the commits above. Two things I did not change
One thing worth flagging @cnavarro-prestashop, you said screenshot and video, and I had only covered the screenshot. The video is the bigger risk and the only file that cannot be edited. It records the whole pass, including the login form with the e-mail typed into it. A screenshot can be retaken or covered with a solid box, a video cannot. The rule now says to decide before the Follow-ups Pre-flight and multistore in a later iteration. I would leave out triage and the The README reflow and the wrapping rule are out, so this PR no longer touches an existing line there. @mattgoud could you re-review when you have a moment, to clear the changes-requested? 🙏 |
There was a problem hiding this comment.
Re-reviewed at ea0f461. All seven findings are addressed, and I checked them in the code rather than from the commit messages.
The blocker is fixed at both ends: run.js reads rendered, and report.js recomputes ok from responds / rendered / overflowPx instead of trusting the phase file. I replayed the case that produced it, a healthy narrow page whose phase.json carries no ok key at all, and the row now renders fine with ok / ok where it used to render pre-existing with an empty reason. An old run directory therefore reports correctly too.
The back-office surface is now gated on a flag set by loginBO(), with the login-form detection as a second line, which is the right shape: the failure it prevents is a page recorded green that nobody ever saw. The header redaction became a pattern rather than a name, the promise guard names which of the two was passed, and the evidence review now covers the video, which is the part I had not thought through: it cannot be edited after the fact, so deciding at the end costs a re-run. Good catch to fold in.
The two things you did not change are argued, and I agree with both. Truncating a response body would destroy the measurement for an endpoint PR, and a mandatory inspection step plus "not a shop with real customer data" is the right trade. Your reading of triage is consistent with the rule you already have, that a check failing in both phases is pre-existing.
Nothing new broken in the delta as far as I can see. Pre-flight and multistore stay on the follow-up list, neither blocks this.
thanks @tblivet 🙏
qadomain with one developer skill,prestashop-pr-qa. It QAs a pull request in a real browser against an already-running shop: reads the PR and its linked issue, reproduces the bug on the pre-fix code, verifies the fix on the PR's code, and writes a report stating approved / not approved / not reproducible with video and per-step screenshots as proof. Works for core, modules, themes and libraries, front office and back office.The skill is prose-only (
SKILL.mdplus tworeferences/files) it ships no executable code. The developer switches the shop between the two states; the skill prints the commands, waits, then verifies the state actually changed before measuring. Nothing is ever posted to GitHub.Also updates
README.md: new domain section, inventory row, directory tree, and a contribution step recommendingreferences/for long knowledge soSKILL.mdstays focused.qa/devnpx skills install PrestaShop/skills/qa/dev, then with a PrestaShop shop running, either invoke it directly in Claude Code:/prestashop-pr-qaor just describe the task:QA the PR PrestaShop/hummingbird#1092 against http://localhost:8887The skill asks for the shop URL, asks you to put the checkout on the PR's merge base, runs the reproduction, asks you to switch to the PR branch, runs the verification, and writes
report.mdwith the verdict plusbefore/andafter/videos and screenshots.Validated on
PrestaShop/hummingbird#1092(keyboard accordion toggles): reproduced on the merge base, confirmed fixed on the PR head, verdict approved.