- Playlists: a link over a playlist the account does not hold plays alone; the rail's own fallback no longer counts as a pick that gives the player up. - The linked opener opens or clears nothing once the address names another player, and stops looking (and fetching its row) once it settled. - Files: a list the server's cap cut — the sorted or searched one too — keeps the link, with a notice of its own (EN/HU). - History: an in-place view rewrite (search refine, count, channel to channel, sub-view) drops a reloaded entry's overlay marks (replaceView); a landing on a channel opened from a search keeps the search; the Feed press on a search opens the plain feed. - Files/Playlists: an emptied search is written at once. - Client log: plf/cf (column text filters) are typed keys; the shell has none of its own. - Contract: writers outside lib/ take a literal or a lib/ function's result; every URL key is sorted typed or not; the writer regexes are held by a fixture; sub-views named; the bookmark net reads page tags. - Feed defaults in one constant; one live-search queue helper; Scheduler writes through the Activity codec; notRun copy (EN/HU) names Forward and a plain "Search YouTube" button.
44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
// @vitest-environment jsdom
|
|
//
|
|
// A reload under two overlays, in a tab whose entries below the dead one are not this document's
|
|
// (Chrome dropped them, see backStaysInDocument): closing the reopened player cannot step over both,
|
|
// so it steps back one — onto the dead entry, still in the app — rather than leaving it. Its own file:
|
|
// the entry is read as the module loads, so it is set up before the import.
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { act, renderHook } from "@testing-library/react";
|
|
|
|
afterEach(() => {
|
|
Reflect.deleteProperty(window, "navigation");
|
|
});
|
|
|
|
describe("an overlay reopened over a dead entry with nothing of this document below it", () => {
|
|
it("closes one step back, staying in the document", async () => {
|
|
window.history.replaceState({ sfPage: "feed", _ov: 1, _ovd: 1 }, "", "/?page=feed&watch=v1");
|
|
window.history.pushState({ sfPage: "feed", _ov: 2, _ovd: 2 }, "", "/?page=feed&watch=v1");
|
|
const { useBackToClose } = await import("./history");
|
|
// Two steps back would leave the document; one stays in it.
|
|
Object.defineProperty(window, "navigation", {
|
|
configurable: true,
|
|
value: {
|
|
currentEntry: { index: 2 },
|
|
entries: () => [{ sameDocument: false }, { sameDocument: true }, { sameDocument: true }],
|
|
},
|
|
});
|
|
const go = vi.spyOn(window.history, "go");
|
|
const back = vi.spyOn(window.history, "back");
|
|
|
|
const r = renderHook(() => useBackToClose(() => {}, { watch: "v1" }));
|
|
await act(async () => {
|
|
await Promise.resolve();
|
|
});
|
|
await act(async () => {
|
|
const landed = new Promise((res) => window.addEventListener("popstate", res, { once: true }));
|
|
r.unmount();
|
|
await landed;
|
|
});
|
|
expect(go).not.toHaveBeenCalled();
|
|
expect(back).toHaveBeenCalledTimes(1);
|
|
expect(window.history.state?._ov).toBe(1);
|
|
});
|
|
});
|