fix(feed): a live search whose first page failed offers Retry where trying again can help
Search YouTube again with the same term read the failure from the cache and sent nothing, so a busy or unreachable YouTube left the search on its error until the term changed. The first page's failure is now the error state with Retry, offered only where a retry can answer (not the day's cap, the feature turned off or the shared budget).
This commit is contained in:
@@ -624,7 +624,16 @@ export default function Feed({
|
||||
{ytQuery.isLoading ? (
|
||||
<div className="py-16 text-center text-muted">{t("feed.yt.searching")}</div>
|
||||
) : ytError && !ytItems.length ? (
|
||||
<div className="py-16 text-center text-muted max-w-md mx-auto">{ytError}</div>
|
||||
// A first page that failed is said as the error it is, with Retry where trying again can
|
||||
// help (YouTube busy or unreachable): Search YouTube again with the same term reads the
|
||||
// failure from the cache and sends nothing.
|
||||
<StateMessage
|
||||
tone="error"
|
||||
className="max-w-md mx-auto"
|
||||
onRetry={ytFinal ? undefined : () => void ytQuery.refetch()}
|
||||
>
|
||||
{ytError}
|
||||
</StateMessage>
|
||||
) : ytItems.length === 0 ? (
|
||||
<div className="py-16 text-center text-muted">{t("feed.yt.noResults")}</div>
|
||||
) : (
|
||||
|
||||
@@ -146,6 +146,38 @@ describe("Feed — a live search's Load more that fails", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Feed — a live search whose first page fails", () => {
|
||||
// Search YouTube again with the same term reads the cached failure: Retry is the way to ask again.
|
||||
it("offers Retry where trying again can help, and reads the page on it", async () => {
|
||||
const search = vi
|
||||
.spyOn(api, "searchYoutube")
|
||||
.mockRejectedValueOnce(
|
||||
new HttpError(429, "Too many searches", { code: "refused", reason: "search_busy" }),
|
||||
)
|
||||
.mockResolvedValueOnce({ items: [hit("a")], next_cursor: null, source: "scrape" } as never);
|
||||
mount();
|
||||
expect(await screen.findByText("Too many searches")).toBeTruthy();
|
||||
fireEvent.click(screen.getByRole("button", { name: "common.retry" }));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByRole("list", { name: "results" }).textContent).toBe("hit a (new)"),
|
||||
);
|
||||
expect(search).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("offers no Retry after a refusal a retry cannot answer", async () => {
|
||||
vi.spyOn(api, "searchYoutube").mockRejectedValue(
|
||||
new HttpError(429, "Today's limit", {
|
||||
code: "refused",
|
||||
reason: "search_daily_limit",
|
||||
limit: 2,
|
||||
}),
|
||||
);
|
||||
mount();
|
||||
expect(await screen.findByText("Today's limit")).toBeTruthy();
|
||||
expect(screen.queryByRole("button", { name: "common.retry" })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Feed — a live search's Load more on its way", () => {
|
||||
// Marked in this session, then changed back elsewhere: a search read after the mark carries the
|
||||
// server's word.
|
||||
|
||||
Reference in New Issue
Block a user