Add configurable skip delay (0-30s) for Skip Intro and Skip Credits
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const { loadContentScript, wait } = require("./helpers");
|
||||
|
||||
test("zero delay still clicks immediately (default behavior preserved)", async () => {
|
||||
const { window } = loadContentScript({
|
||||
url: "https://app.plex.tv/web/index.html",
|
||||
html: `<!DOCTYPE html><html><body><button>Skip Intro</button></body></html>`,
|
||||
settings: { introDelaySeconds: 0 }
|
||||
});
|
||||
|
||||
await wait(50);
|
||||
assert.strictEqual(window.__clickedElements.length, 1);
|
||||
});
|
||||
|
||||
test("does not click immediately when a delay is configured, but clicks once it elapses", async () => {
|
||||
const { window } = loadContentScript({
|
||||
url: "https://app.plex.tv/web/index.html",
|
||||
html: `<!DOCTYPE html><html><body><button>Skip Credits</button></body></html>`,
|
||||
settings: { creditsDelaySeconds: 1 }
|
||||
});
|
||||
|
||||
await wait(50);
|
||||
assert.strictEqual(window.__clickedElements.length, 0, "should not click before the delay elapses");
|
||||
|
||||
await wait(1100);
|
||||
assert.strictEqual(window.__clickedElements.length, 1, "should click once the delay elapses");
|
||||
});
|
||||
|
||||
test("does not schedule duplicate clicks while a delay is already pending", async () => {
|
||||
const { window } = loadContentScript({
|
||||
url: "https://app.plex.tv/web/index.html",
|
||||
html: `<!DOCTYPE html><html><body><button id="skip">Skip Intro</button></body></html>`,
|
||||
settings: { introDelaySeconds: 1 }
|
||||
});
|
||||
|
||||
await wait(50);
|
||||
window.document.body.appendChild(window.document.createElement("div"));
|
||||
await wait(50);
|
||||
window.document.body.appendChild(window.document.createElement("div"));
|
||||
|
||||
await wait(1200);
|
||||
assert.strictEqual(window.__clickedElements.length, 1);
|
||||
});
|
||||
|
||||
test("cancels a scheduled click if its toggle is turned off mid-delay", async () => {
|
||||
const { window, fireStorageChange } = loadContentScript({
|
||||
url: "https://app.plex.tv/web/index.html",
|
||||
html: `<!DOCTYPE html><html><body><button>Skip Intro</button></body></html>`,
|
||||
settings: { introDelaySeconds: 1 }
|
||||
});
|
||||
|
||||
await wait(50);
|
||||
fireStorageChange({ skipIntro: false });
|
||||
|
||||
await wait(1200);
|
||||
assert.strictEqual(window.__clickedElements.length, 0);
|
||||
});
|
||||
|
||||
test("does not click if the button is removed from the page before the delay elapses", async () => {
|
||||
const { window } = loadContentScript({
|
||||
url: "https://app.plex.tv/web/index.html",
|
||||
html: `<!DOCTYPE html><html><body><button id="skip">Skip Intro</button></body></html>`,
|
||||
settings: { introDelaySeconds: 1 }
|
||||
});
|
||||
|
||||
await wait(50);
|
||||
window.document.getElementById("skip").remove();
|
||||
|
||||
await wait(1200);
|
||||
assert.strictEqual(window.__clickedElements.length, 0);
|
||||
});
|
||||
|
||||
test("does not click if the delayed button is hidden before the delay elapses", async () => {
|
||||
const { window } = loadContentScript({
|
||||
url: "https://app.plex.tv/web/index.html",
|
||||
html: `<!DOCTYPE html><html><body><button id="skip">Skip Intro</button></body></html>`,
|
||||
settings: { introDelaySeconds: 1 }
|
||||
});
|
||||
|
||||
await wait(50);
|
||||
window.document.getElementById("skip").style.display = "none";
|
||||
|
||||
await wait(1200);
|
||||
assert.strictEqual(window.__clickedElements.length, 0);
|
||||
});
|
||||
|
||||
test("intro and credits delays run independently", async () => {
|
||||
const { window } = loadContentScript({
|
||||
url: "https://app.plex.tv/web/index.html",
|
||||
html: `<!DOCTYPE html><html><body>
|
||||
<button id="intro">Skip Intro</button>
|
||||
<button id="credits">Skip Credits</button>
|
||||
</body></html>`,
|
||||
settings: { introDelaySeconds: 0, creditsDelaySeconds: 1 }
|
||||
});
|
||||
|
||||
await wait(50);
|
||||
assert.strictEqual(window.__clickedElements.length, 1, "Skip Intro should click immediately");
|
||||
assert.strictEqual(window.__clickedElements[0], window.document.getElementById("intro"));
|
||||
|
||||
await wait(1100);
|
||||
assert.strictEqual(window.__clickedElements.length, 2, "Skip Credits should click after its own delay");
|
||||
});
|
||||
+26
-3
@@ -10,7 +10,13 @@ function loadContentScript({ url, html, settings }) {
|
||||
|
||||
const window = dom.window;
|
||||
const storedSettings = Object.assign(
|
||||
{ plexUrl: "https://app.plex.tv", skipIntro: true, skipCredits: true },
|
||||
{
|
||||
plexUrl: "https://app.plex.tv",
|
||||
skipIntro: true,
|
||||
skipCredits: true,
|
||||
introDelaySeconds: 0,
|
||||
creditsDelaySeconds: 0
|
||||
},
|
||||
settings || {}
|
||||
);
|
||||
const changeListeners = [];
|
||||
@@ -57,7 +63,13 @@ function loadPopupScript({ initialSettings } = {}) {
|
||||
const dom = new JSDOM(html, { url: "chrome-extension://fake/popup.html", runScripts: "dangerously" });
|
||||
const window = dom.window;
|
||||
const storedSettings = Object.assign(
|
||||
{ plexUrl: "https://app.plex.tv", skipIntro: true, skipCredits: true },
|
||||
{
|
||||
plexUrl: "https://app.plex.tv",
|
||||
skipIntro: true,
|
||||
skipCredits: true,
|
||||
introDelaySeconds: 0,
|
||||
creditsDelaySeconds: 0
|
||||
},
|
||||
initialSettings || {}
|
||||
);
|
||||
const savedCalls = [];
|
||||
@@ -82,8 +94,19 @@ function loadPopupScript({ initialSettings } = {}) {
|
||||
return { dom, window, savedCalls, storedSettings };
|
||||
}
|
||||
|
||||
function loadUtils() {
|
||||
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>", {
|
||||
url: "https://example.com",
|
||||
runScripts: "dangerously"
|
||||
});
|
||||
const window = dom.window;
|
||||
const utilsCode = fs.readFileSync(path.join(__dirname, "..", "utils.js"), "utf8");
|
||||
window.eval(utilsCode);
|
||||
return window;
|
||||
}
|
||||
|
||||
function wait(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
module.exports = { loadContentScript, loadPopupScript, wait };
|
||||
module.exports = { loadContentScript, loadPopupScript, loadUtils, wait };
|
||||
|
||||
+42
-1
@@ -2,7 +2,7 @@ const test = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const { loadPopupScript, wait } = require("./helpers");
|
||||
|
||||
test("defaults to app.plex.tv preset with both toggles on", async () => {
|
||||
test("defaults to app.plex.tv preset with both toggles on and delays at 0s", async () => {
|
||||
const { window } = loadPopupScript();
|
||||
await wait(20);
|
||||
|
||||
@@ -10,6 +10,47 @@ test("defaults to app.plex.tv preset with both toggles on", async () => {
|
||||
assert.strictEqual(window.document.getElementById("skipIntro").checked, true);
|
||||
assert.strictEqual(window.document.getElementById("skipCredits").checked, true);
|
||||
assert.strictEqual(window.document.getElementById("customUrlRow").style.display, "none");
|
||||
assert.strictEqual(window.document.getElementById("introDelay").value, "0");
|
||||
assert.strictEqual(window.document.getElementById("creditsDelay").value, "0");
|
||||
assert.strictEqual(window.document.getElementById("introDelayValue").textContent, "0s");
|
||||
assert.strictEqual(window.document.getElementById("creditsDelayValue").textContent, "0s");
|
||||
});
|
||||
|
||||
test("loads a previously saved delay value onto the sliders", async () => {
|
||||
const { window } = loadPopupScript({
|
||||
initialSettings: { introDelaySeconds: 3, creditsDelaySeconds: 12 }
|
||||
});
|
||||
await wait(20);
|
||||
|
||||
assert.strictEqual(window.document.getElementById("introDelay").value, "3");
|
||||
assert.strictEqual(window.document.getElementById("introDelayValue").textContent, "3s");
|
||||
assert.strictEqual(window.document.getElementById("creditsDelay").value, "12");
|
||||
assert.strictEqual(window.document.getElementById("creditsDelayValue").textContent, "12s");
|
||||
});
|
||||
|
||||
test("disables a delay slider when its skip toggle is off", async () => {
|
||||
const { window } = loadPopupScript({
|
||||
initialSettings: { skipCredits: false }
|
||||
});
|
||||
await wait(20);
|
||||
|
||||
assert.strictEqual(window.document.getElementById("introDelay").disabled, false);
|
||||
assert.strictEqual(window.document.getElementById("creditsDelay").disabled, true);
|
||||
});
|
||||
|
||||
test("saving persists the configured delay values", async () => {
|
||||
const { window, savedCalls } = loadPopupScript();
|
||||
await wait(20);
|
||||
|
||||
window.document.getElementById("introDelay").value = "5";
|
||||
window.document.getElementById("creditsDelay").value = "20";
|
||||
|
||||
window.document.getElementById("saveBtn").click();
|
||||
await wait(20);
|
||||
|
||||
assert.strictEqual(savedCalls.length, 1);
|
||||
assert.strictEqual(savedCalls[0].introDelaySeconds, 5);
|
||||
assert.strictEqual(savedCalls[0].creditsDelaySeconds, 20);
|
||||
});
|
||||
|
||||
test("shows custom URL field pre-filled when a custom URL is already saved", async () => {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert");
|
||||
const { loadUtils } = require("./helpers");
|
||||
|
||||
test("clampDelaySeconds keeps in-range integer values as-is", () => {
|
||||
const window = loadUtils();
|
||||
assert.strictEqual(window.clampDelaySeconds(0), 0);
|
||||
assert.strictEqual(window.clampDelaySeconds(5), 5);
|
||||
assert.strictEqual(window.clampDelaySeconds(30), 30);
|
||||
});
|
||||
|
||||
test("clampDelaySeconds clamps below 0 and above 30", () => {
|
||||
const window = loadUtils();
|
||||
assert.strictEqual(window.clampDelaySeconds(-5), 0);
|
||||
assert.strictEqual(window.clampDelaySeconds(45), 30);
|
||||
});
|
||||
|
||||
test("clampDelaySeconds rounds fractional values", () => {
|
||||
const window = loadUtils();
|
||||
assert.strictEqual(window.clampDelaySeconds(15.6), 16);
|
||||
assert.strictEqual(window.clampDelaySeconds(15.4), 15);
|
||||
});
|
||||
|
||||
test("clampDelaySeconds falls back to 0 for non-numeric input", () => {
|
||||
const window = loadUtils();
|
||||
assert.strictEqual(window.clampDelaySeconds("abc"), 0);
|
||||
assert.strictEqual(window.clampDelaySeconds(undefined), 0);
|
||||
assert.strictEqual(window.clampDelaySeconds(null), 0);
|
||||
assert.strictEqual(window.clampDelaySeconds(NaN), 0);
|
||||
});
|
||||
Reference in New Issue
Block a user