fix(sources): Manger Bouger sans filtre déroule tout le catalogue (#78)
All checks were successful
All checks were successful
Le parcours « tout voir » (filtre texte vide) de la source Manger Bouger s'arrêtait après la 1re page (9 recettes) sur le serveur déployé. Cause : `list()` ajoutait toujours un `&query=` final, même vide. Sur le serveur déployé, `…/recettes?diet=ALL&page=N&query=` renvoyait la page 1 pour tout `N`, et le garde-fou anti-boucle de #38 (« 0 nouvel externalId ⇒ fin de liste ») coupait le défilement. La requête catalogue nue `?diet=ALL&page=N` pagine correctement. - `manger-bouger.ts` `list()` : n'ajoute `&query=` que si le terme de recherche est non vide (après `trim`), comme les adaptateurs voisins (marmiton / quitoque / ohmonbento). Le chemin filtré ne change pas. - Commentaires `LIST_URL` / `hasMorePages` corrigés : la pagination « vérifiée en direct » ne valait que pour le chemin avec `query` texte. - Tests `manger-bouger.test.ts` : `list({})` construit une URL sans paramètre `query` ; un `query` blanc = pas de filtre ; le catalogue nu pagine quand même (`nextCursor`) ; `list({ query: salade })` ajoute toujours `&query=salade` (non-régression). Closes #78. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
46c23c24ab
commit
7d20404b6f
2 changed files with 73 additions and 8 deletions
|
|
@ -18,11 +18,13 @@ const SOURCE_KEY = "mangerBouger";
|
||||||
// all (unlike marmiton.ts's search page) — but it's server-rendered, and a
|
// all (unlike marmiton.ts's search page) — but it's server-rendered, and a
|
||||||
// plain GET carries the exact same Redux state the client hydrates from as
|
// plain GET carries the exact same Redux state the client hydrates from as
|
||||||
// a `__NEXT_DATA__` script tag (see `extractNextData` below), which already
|
// a `__NEXT_DATA__` script tag (see `extractNextData` below), which already
|
||||||
// has everything `list()` needs. Verified live: `?query=<free text>` really
|
// has everything `list()` needs. Verified live for the *search* path
|
||||||
// filters server-side (not just a client-side URL update over an
|
// (`?diet=ALL&page=N&query=<free text>`): `query` really filters
|
||||||
// already-fetched page), and `page`/`hasMorePages` behave as real,
|
// server-side (not just a client-side URL update over an already-fetched
|
||||||
// consistent pagination — the best-behaved of this adapter family's three
|
// page), and `page`/`hasMorePages` behave as real, consistent pagination.
|
||||||
// sources on that front.
|
// The *catalogue browse* path (no `query` term) must be the bare
|
||||||
|
// `?diet=ALL&page=N` — a trailing empty `&query=` made the deployed server
|
||||||
|
// repeat page 1 for every page (issue #78); `list()` below now omits it.
|
||||||
const LIST_URL = "https://www.mangerbouger.fr/manger-mieux/la-fabrique-a-menus/recettes";
|
const LIST_URL = "https://www.mangerbouger.fr/manger-mieux/la-fabrique-a-menus/recettes";
|
||||||
const DETAIL_BASE_URL = "https://www.mangerbouger.fr/manger-mieux/la-fabrique-a-menus/recettes/";
|
const DETAIL_BASE_URL = "https://www.mangerbouger.fr/manger-mieux/la-fabrique-a-menus/recettes/";
|
||||||
|
|
||||||
|
|
@ -42,7 +44,7 @@ interface MangerBougerListPageData {
|
||||||
initialState?: {
|
initialState?: {
|
||||||
recipes?: {
|
recipes?: {
|
||||||
list?: MangerBougerListEntry[];
|
list?: MangerBougerListEntry[];
|
||||||
/** Whether a further page exists for the current `page`/`query`/`diet` combination — verified live: an out-of-range page comes back `false` with an empty `list` rather than repeating the last page or erroring, a cleaner end-of-results signal than either `marmiton.ts` (infers it from a 404) or `750g.ts` (this search has no real pagination at all). */
|
/** Whether a further page exists for the current `page`/`query`/`diet` combination — verified live on the `query`-term path: an out-of-range page comes back `false` with an empty `list` rather than repeating the last page or erroring, a cleaner end-of-results signal than either `marmiton.ts` (infers it from a 404) or `750g.ts` (this search has no real pagination at all). The catalogue-browse path (no `query`) only paginates when the request URL omits `&query=` entirely — see `LIST_URL` and issue #78. */
|
||||||
hasMorePages?: boolean;
|
hasMorePages?: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -284,8 +286,18 @@ export const mangerBougerAdapter: RecipeSourceAdapter<{ html: string; url: strin
|
||||||
async list(params: RecipeSourceListParams): Promise<RecipeSourceListResult> {
|
async list(params: RecipeSourceListParams): Promise<RecipeSourceListResult> {
|
||||||
try {
|
try {
|
||||||
const page = params.cursor ? Number(params.cursor) : 1;
|
const page = params.cursor ? Number(params.cursor) : 1;
|
||||||
const query = params.query ?? "";
|
// Only append `&query=` when there's an actual search term. A trailing
|
||||||
const listUrl = `${LIST_URL}?diet=ALL&page=${page}&query=${encodeURIComponent(query)}`;
|
// empty `&query=` on the "browse everything" path (filter absent) made
|
||||||
|
// the *deployed* server serve page 1 for every `page=N` — the
|
||||||
|
// infinite scroll then stopped after 9 recipes (issue #38's
|
||||||
|
// "0 new externalIds ⇒ end of list" guard cutting in). The bare
|
||||||
|
// `?diet=ALL&page=N` catalogue request paginates correctly. The
|
||||||
|
// sibling adapters (marmiton/quitoque/ohmonbento) already make their
|
||||||
|
// search param conditional; this one didn't.
|
||||||
|
const query = (params.query ?? "").trim();
|
||||||
|
const listUrl = `${LIST_URL}?diet=ALL&page=${page}${
|
||||||
|
query ? `&query=${encodeURIComponent(query)}` : ""
|
||||||
|
}`;
|
||||||
|
|
||||||
let response: Response;
|
let response: Response;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,59 @@ describe("mangerBougerAdapter", () => {
|
||||||
expect(requestedUrl).to.include("query=cr%C3%A8me%20br%C3%BBl%C3%A9e");
|
expect(requestedUrl).to.include("query=cr%C3%A8me%20br%C3%BBl%C3%A9e");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("omits the query param entirely on the catalogue-browse path (no filter) — issue #78", async () => {
|
||||||
|
let requestedUrl: string | undefined;
|
||||||
|
globalThis.fetch = (async (url: string) => {
|
||||||
|
requestedUrl = url;
|
||||||
|
return new Response(htmlWithListNextData({ list: [], hasMorePages: true }), {
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
}) as typeof fetch;
|
||||||
|
|
||||||
|
await mangerBougerAdapter.list({});
|
||||||
|
|
||||||
|
expect(requestedUrl).to.equal(
|
||||||
|
"https://www.mangerbouger.fr/manger-mieux/la-fabrique-a-menus/recettes?diet=ALL&page=1",
|
||||||
|
);
|
||||||
|
expect(requestedUrl).not.to.include("query=");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("treats a blank/whitespace query the same as no filter (no query param)", async () => {
|
||||||
|
let requestedUrl: string | undefined;
|
||||||
|
globalThis.fetch = (async (url: string) => {
|
||||||
|
requestedUrl = url;
|
||||||
|
return new Response(htmlWithListNextData({ list: [], hasMorePages: false }), {
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
}) as typeof fetch;
|
||||||
|
|
||||||
|
await mangerBougerAdapter.list({ query: " " });
|
||||||
|
|
||||||
|
expect(requestedUrl).not.to.include("query=");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still paginates the catalogue-browse path (nextCursor set when hasMorePages)", async () => {
|
||||||
|
stubFetchHtml(htmlWithListNextData({ list: [], hasMorePages: true }));
|
||||||
|
|
||||||
|
const result = await mangerBougerAdapter.list({});
|
||||||
|
|
||||||
|
expect(result.nextCursor).to.equal("2");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("still appends the query param when a real search term is given", async () => {
|
||||||
|
let requestedUrl: string | undefined;
|
||||||
|
globalThis.fetch = (async (url: string) => {
|
||||||
|
requestedUrl = url;
|
||||||
|
return new Response(htmlWithListNextData({ list: [], hasMorePages: false }), {
|
||||||
|
status: 200,
|
||||||
|
});
|
||||||
|
}) as typeof fetch;
|
||||||
|
|
||||||
|
await mangerBougerAdapter.list({ query: "salade" });
|
||||||
|
|
||||||
|
expect(requestedUrl).to.include("&query=salade");
|
||||||
|
});
|
||||||
|
|
||||||
it("skips a list entry missing a slug or a name", async () => {
|
it("skips a list entry missing a slug or a name", async () => {
|
||||||
stubFetchHtml(
|
stubFetchHtml(
|
||||||
htmlWithListNextData({
|
htmlWithListNextData({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue