fix(recipes): stoppe le scroll infini des sources au lieu de boucler #46

Merged
kyuno merged 3 commits from fix/source-scroll-loop into main 2026-09-05 21:00:23 +02:00
2 changed files with 33 additions and 15 deletions
Showing only changes of commit 1cd886cb8a - Show all commits

View file

@ -64,11 +64,9 @@ Feature: Browsing external recipe sources
When I visit "/recettes"
And I click the button "TheMealDB"
Then I should see the source item "Page 1 Item 1"
When I scroll the source list to the bottom
Then I should see the source item "Page 2 Item 1"
When I scroll the source list to the bottom
Then I should see the source item "Page 3 Item 1"
And no browse cursor should have been requested more than once
When I scroll the source list to the bottom 5 times
Then the third page's items should have loaded
And no browse cursor should have been requested more than twice
Scenario: Offers a retry when loading the next page fails
Given the recipe catalog contains nothing

View file

@ -174,18 +174,38 @@ Given("browsing TheMealDB returns three pages, each addressed by its own cursor"
});
});
When("I scroll the source list to the bottom", () => {
cy.get(".source-items-column .recipe-table-wrap").scrollTo("bottom", {
ensureScrollable: false,
});
// IntersectionObserver callbacks are delivered on their own task, and the
// next page's fetch + merge then needs a render — give both a beat.
cy.wait(400);
When("I scroll the source list to the bottom {int} times", (times: number) => {
for (let attempt = 0; attempt < times; attempt += 1) {
cy.get(".source-items-column .recipe-table-wrap").scrollTo("bottom", {
ensureScrollable: false,
});
// IntersectionObserver callbacks land on their own task, and the next
// page's fetch + merge then needs a render — give both a beat before
// the next scroll (which needs fresh content below it to have anything
// new to reveal).
cy.wait(350);
}
});
Then("no browse cursor should have been requested more than once", () => {
const seen = [...threePageCursorsRequested];
expect(seen.length, `cursors requested: ${JSON.stringify(seen)}`).to.equal(new Set(seen).size);
Then("the third page's items should have loaded", () => {
// Page 3's marker sits ~30 rows down inside the list's own overflow
// container — scroll it in before asserting, like `common.steps.ts`'s
// ingredient-row check does. With the stale-closure loop it never loads
// at all: `handleLoadMore` keeps re-requesting cursor-2.
cy.contains(".recipe-table__name", "Page 3 Item 1").scrollIntoView().should("be.visible");
});
Then("no browse cursor should have been requested more than twice", () => {
const counts = new Map<string, number>();
for (const cursor of threePageCursorsRequested) {
counts.set(cursor, (counts.get(cursor) ?? 0) + 1);
}
// React StrictMode double-invokes effects in the e2e dev server (see
// recipes.cy.ts), so the panel's initial fetch + one-page-ahead prefetch
// can each fire twice — hence "twice", not "once". The issue #38 loop
// blows well past that: every scroll re-requests the same stale cursor.
const offenders = [...counts.entries()].filter(([, n]) => n > 2);
expect(offenders, `cursor request counts: ${JSON.stringify([...counts])}`).to.have.length(0);
});
// The panel prefetches the next page as soon as page 1 is on screen (before