mirror of
https://github.com/Alvin-Zilverstand/return-youtube-dislike.git
synced 2026-10-28 07:40:27 +01:00
e500088e65
Add Firefox data consent, repair contributor sign-in, and introduce optional Hide clutter buttons. Harden video navigation, voting, and action layouts with shared browser regression coverage and reproducible release packaging.
121 lines
4.8 KiB
JavaScript
121 lines
4.8 KiB
JavaScript
const { AttributedRuntimeTrafficLedger, attributeRuntimeRequest } = require("../e2e/live/live-youtube-driver");
|
|
|
|
const SELECTED_EXTENSION_ID = "abcdefghijklmnopabcdefghijklmnop";
|
|
const EXPECTED_BUILD_ID = "0123456789abcdef0123456789abcdef";
|
|
|
|
function createEventContext(pages = []) {
|
|
const listeners = new Map();
|
|
return {
|
|
context: {
|
|
off: jest.fn((event, listener) => listeners.get(event)?.delete(listener)),
|
|
on: jest.fn((event, listener) => {
|
|
if (!listeners.has(event)) listeners.set(event, new Set());
|
|
listeners.get(event).add(listener);
|
|
}),
|
|
pages: jest.fn(() => pages),
|
|
},
|
|
emit(event, value) {
|
|
listeners.get(event)?.forEach((listener) => listener(value));
|
|
},
|
|
listeners,
|
|
};
|
|
}
|
|
|
|
function pageRequest(page, { method, pathname, videoId }) {
|
|
return {
|
|
frame: () => ({ page: () => page }),
|
|
method: () => method,
|
|
postDataJSON: () => ({ userId: "A".repeat(36), value: -1, videoId }),
|
|
url: () => `https://returnyoutubedislikeapi.com${pathname}${pathname === "/votes" ? `?videoId=${videoId}` : ""}`,
|
|
};
|
|
}
|
|
|
|
async function advanceFakeTime(milliseconds) {
|
|
for (let elapsed = 0; elapsed < milliseconds; elapsed += 25) {
|
|
jest.advanceTimersByTime(Math.min(25, milliseconds - elapsed));
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
}
|
|
}
|
|
|
|
describe("run-wide attributed runtime traffic ledger", () => {
|
|
afterEach(() => {
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
test.each([
|
|
["late vote-data GET", { method: "GET", pathname: "/votes" }],
|
|
["late interaction POST", { method: "POST", pathname: "/interact/vote" }],
|
|
])("rejects a %s emitted after the stage listener detached", async (_label, requestOptions) => {
|
|
jest.useFakeTimers({ doNotFake: ["performance"] });
|
|
const page = {};
|
|
const harness = createEventContext([page]);
|
|
const ledger = new AttributedRuntimeTrafficLedger(harness.context, page, (request) =>
|
|
attributeRuntimeRequest(request, page, "userscript"),
|
|
);
|
|
const detachedStageListener = jest.fn();
|
|
harness.context.on("request", detachedStageListener);
|
|
harness.context.off("request", detachedStageListener);
|
|
const request = pageRequest(page, { ...requestOptions, videoId: "abcdefghijk" });
|
|
|
|
const audit = ledger.assertNoUnclaimed({ label: "detached stage", quietMs: 100, timeoutMs: 500 });
|
|
const rejection = expect(audit).rejects.toThrow(/escaped every active audit/);
|
|
setTimeout(() => harness.emit("request", request), 50);
|
|
await advanceFakeTime(250);
|
|
await rejection;
|
|
|
|
expect(detachedStageListener).not.toHaveBeenCalled();
|
|
expect(ledger.records).toEqual([
|
|
expect.objectContaining({
|
|
method: requestOptions.method,
|
|
pathname: requestOptions.pathname,
|
|
videoId: "abcdefghijk",
|
|
}),
|
|
]);
|
|
ledger.stop();
|
|
});
|
|
|
|
test("accepts delayed traffic only when an active stage claims that exact request", async () => {
|
|
jest.useFakeTimers({ doNotFake: ["performance"] });
|
|
const page = {};
|
|
const harness = createEventContext([page]);
|
|
const ledger = new AttributedRuntimeTrafficLedger(harness.context, page, (request) =>
|
|
attributeRuntimeRequest(request, page, "userscript"),
|
|
);
|
|
const request = pageRequest(page, { method: "GET", pathname: "/votes", videoId: "abcdefghijk" });
|
|
harness.context.on("request", (candidate) => ledger.claim(candidate, "exact-votes-stage"));
|
|
|
|
const audit = ledger.assertNoUnclaimed({ label: "claimed stage", quietMs: 100, timeoutMs: 500 });
|
|
setTimeout(() => harness.emit("request", request), 50);
|
|
await advanceFakeTime(250);
|
|
|
|
await expect(audit).resolves.toEqual({ attributedRequests: 1, label: "claimed stage" });
|
|
ledger.stop();
|
|
});
|
|
|
|
test("retains same-video other-tab ambiguity from request time after that tab navigates away", async () => {
|
|
const page = { url: () => "https://www.youtube.com/watch?v=abcdefghijk" };
|
|
let otherUrl = "https://www.youtube.com/shorts/abcdefghijk";
|
|
const otherPage = { url: () => otherUrl };
|
|
const harness = createEventContext([page, otherPage]);
|
|
const ledger = new AttributedRuntimeTrafficLedger(harness.context, page, (request) =>
|
|
attributeRuntimeRequest(request, page, "extension", SELECTED_EXTENSION_ID, EXPECTED_BUILD_ID),
|
|
);
|
|
const request = {
|
|
frame: () => {
|
|
throw new Error("Service Worker requests do not have a frame");
|
|
},
|
|
method: () => "GET",
|
|
serviceWorker: () => ({ url: () => `chrome-extension://${SELECTED_EXTENSION_ID}/ryd.background.js` }),
|
|
url: () => "https://returnyoutubedislikeapi.com/votes?videoId=abcdefghijk",
|
|
};
|
|
|
|
harness.emit("request", request);
|
|
expect(ledger.claim(request, "exact-votes-stage")).toBe(true);
|
|
otherUrl = "https://www.youtube.com/watch?v=lmnopqrstuv";
|
|
|
|
await expect(ledger.assertNoUnclaimed({ quietMs: 0 })).rejects.toThrow(/same video open at send time/);
|
|
ledger.stop();
|
|
});
|
|
});
|