Files
return-youtube-dislike/Extensions/UserScript/live/live-runtime-traffic-ledger.spec.js
T

121 lines
4.8 KiB
JavaScript
Raw Normal View History

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();
});
});