fix(feed): an override yields only to a refetch that set out after its write was answered
Send order let a refetch read before an unanswered mark committed and drop it. A reset's position 0 is written again after a refetch that set out before the server took the reset. The player-session refetch also waits for the player's closing checkpoint, which the player now tracks (progressFlushes); refetched first, the list put the older position back.
This commit is contained in:
@@ -40,10 +40,14 @@ vi.mock("./VirtualFeed", () => ({
|
||||
),
|
||||
}));
|
||||
// The player's progress hook marks a clip closed near its end watched from its unmount cleanup:
|
||||
// `watchOnUnmount` stands in for that.
|
||||
const player = vi.hoisted(() => ({ watchOnUnmount: false }));
|
||||
// `watchOnUnmount` stands in for that, and `flush` for its closing checkpoint, which it tracks.
|
||||
const player = vi.hoisted(() => ({
|
||||
watchOnUnmount: false,
|
||||
flush: null as Promise<unknown> | null,
|
||||
}));
|
||||
vi.mock("./PlayerModal", async () => {
|
||||
const { useEffect } = await import("react");
|
||||
const { trackProgressFlush } = await import("../lib/progressCache");
|
||||
return {
|
||||
default: function Player({
|
||||
video,
|
||||
@@ -57,6 +61,7 @@ vi.mock("./PlayerModal", async () => {
|
||||
useEffect(
|
||||
() => () => {
|
||||
if (player.watchOnUnmount) onState(video.id, "watched");
|
||||
if (player.flush) trackProgressFlush(player.flush);
|
||||
},
|
||||
[video.id, onState],
|
||||
);
|
||||
@@ -91,6 +96,7 @@ afterEach(() => {
|
||||
cleanup();
|
||||
vi.restoreAllMocks();
|
||||
player.watchOnUnmount = false;
|
||||
player.flush = null;
|
||||
});
|
||||
|
||||
const FILTERS: FeedFilters = {
|
||||
@@ -203,6 +209,22 @@ describe("Feed — a video marked watched in the player", () => {
|
||||
await waitFor(() => expect(feed.mock.calls.length).toBeGreaterThan(before));
|
||||
});
|
||||
|
||||
it("waits for the player's closing checkpoint before it refetches", async () => {
|
||||
// Refetched before that write landed, the list read the older position and, landing after the
|
||||
// player's patch, put it back: a reopen resumed early.
|
||||
const { feed } = mount();
|
||||
let land: (v: unknown) => void = () => {};
|
||||
player.flush = new Promise((r) => (land = r));
|
||||
fireEvent.click(await screen.findByText("open v1"));
|
||||
fireEvent.click(await screen.findByText("mark"));
|
||||
const before = feed.mock.calls.length;
|
||||
fireEvent.click(screen.getByText("close"));
|
||||
await act(() => new Promise((r) => setTimeout(r, 30)));
|
||||
expect(feed.mock.calls.length).toBe(before);
|
||||
await act(async () => land(undefined));
|
||||
await waitFor(() => expect(feed.mock.calls.length).toBeGreaterThan(before));
|
||||
});
|
||||
|
||||
it("does not refetch on a close with nothing changed", async () => {
|
||||
const { feed } = mount();
|
||||
fireEvent.click(await screen.findByText("open v1"));
|
||||
|
||||
Reference in New Issue
Block a user