diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 3636a26..699f867 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -10,8 +10,6 @@ on:
jobs:
build:
runs-on: ubuntu-latest
- env:
- WEB_EXT_IGNORE_FILES: scripts/**
steps:
- uses: actions/checkout@v4
@@ -20,7 +18,7 @@ jobs:
run: npm install -g web-ext
- name: Lint
- run: web-ext lint
+ run: web-ext lint --source-dir extension
# Beta tag (v*-beta) β Sign as unlisted on AMO, attach signed XPI to GitHub Prerelease
# Firefox blocks all unsigned XPIs, even for self-hosted installs β unlisted signing is required
@@ -30,7 +28,7 @@ jobs:
web-ext sign \
--api-key ${{ secrets.FIREFOX_API_KEY }} \
--api-secret ${{ secrets.FIREFOX_API_SECRET }} \
- --source-dir . \
+ --source-dir extension \
--artifacts-dir web-ext-artifacts \
--channel unlisted
@@ -70,6 +68,6 @@ jobs:
web-ext sign \
--api-key ${{ secrets.FIREFOX_API_KEY }} \
--api-secret ${{ secrets.FIREFOX_API_SECRET }} \
- --source-dir . \
+ --source-dir extension \
--artifacts-dir web-ext-artifacts \
--channel listed
diff --git a/README.md b/README.md
index 8362267..5c197bb 100644
--- a/README.md
+++ b/README.md
@@ -67,6 +67,16 @@ listens both for lower and upper case values (i.e. you can use
key. This is not a perfect solution, as some sites may listen to both, but works
most of the time.
+## Development
+
+The unpacked extension root is `extension/`. Load that directory in
+`about:debugging`, and run extension tooling against it, for example:
+
+```sh
+npm test
+npx --yes web-ext lint --source-dir extension
+```
+
## FAQ
### The video controls are not showing up?
diff --git a/build.py b/build.py
deleted file mode 100644
index cc429b0..0000000
--- a/build.py
+++ /dev/null
@@ -1,157 +0,0 @@
-import glob
-import fnmatch
-import os
-import re
-import shutil
-import tempfile
-import zipfile
-
-SCRIPT_NAME = os.path.basename(__file__)
-TARGET_FILE = "manifest.json"
-DEFAULT_EXCLUDE_FILES = {".DS_Store"}
-DEFAULT_EXCLUDE_DIRS = {"__pycache__", "temp"}
-DEFAULT_EXCLUDE_PATTERNS = {"._*", "*.pyc"}
-
-
-def should_exclude(rel_path, exclude_files, exclude_dirs):
- rel_path = os.path.normpath(rel_path)
- path_parts = rel_path.split(os.sep)
- file_name = path_parts[-1]
-
- if file_name in DEFAULT_EXCLUDE_FILES or rel_path in DEFAULT_EXCLUDE_FILES:
- return True
-
- if any(part in DEFAULT_EXCLUDE_DIRS for part in path_parts):
- return True
-
- if file_name in exclude_files or rel_path in exclude_files:
- return True
-
- if any(part in exclude_dirs for part in path_parts):
- return True
-
- if any(fnmatch.fnmatch(file_name, pattern) for pattern in DEFAULT_EXCLUDE_PATTERNS):
- return True
-
- return False
-
-
-def zip_folder(output_name, folder, exclude_files, exclude_dirs):
- with zipfile.ZipFile(output_name, "w", zipfile.ZIP_DEFLATED) as zipf:
- for root, dirs, files in os.walk(folder):
- dirs[:] = [
- d
- for d in dirs
- if not should_exclude(
- os.path.relpath(os.path.join(root, d), folder),
- exclude_files,
- exclude_dirs,
- )
- ]
- for file in files:
- rel_path = os.path.relpath(os.path.join(root, file), folder)
- if should_exclude(rel_path, exclude_files, exclude_dirs):
- continue
- zipf.write(os.path.join(root, file), arcname=rel_path)
-
-
-def update_version_line(file_path, new_version):
- with open(file_path, "r", encoding="utf-8") as f:
- lines = f.readlines()
-
- updated = False
- for i, line in enumerate(lines):
- match = re.match(r'\s*"version":\s*"([^"]+)"', line)
- if match:
- old_version = match.group(1)
- lines[i] = re.sub(
- r'"version":\s*".+?"', f'"version": "{new_version}"', line
- )
- updated = True
- print(
- f"π οΈ Changed version in {file_path} from {old_version} β {new_version}"
- )
- break
-
- if updated:
- with open(file_path, "w", encoding="utf-8") as f:
- f.writelines(lines)
- else:
- print(f"β οΈ No version line found in {file_path}.")
-
-
-def main():
- # Step 0: Remove all existing .xpi files upfront
- xpi_files = glob.glob("*.xpi")
- for f in xpi_files:
- try:
- os.remove(f)
- print(f"ποΈ Removed existing archive: {f}")
- except Exception as e:
- print(f"β οΈ Failed to remove {f}: {e}")
-
- # Read current version from manifest.json
- current_dir = os.getcwd()
- manifest_path = os.path.join(current_dir, TARGET_FILE)
- current_version = "unknown"
-
- if os.path.exists(manifest_path):
- with open(manifest_path, "r", encoding="utf-8") as f:
- for line in f:
- match = re.match(r'\s*"version":\s*"([^"]+)"', line)
- if match:
- current_version = match.group(1)
- break
-
- print(f"π¦ Current version: {current_version}")
- base_version = input("Enter the new base version (e.g., 2.0.1): ").strip()
- if not base_version:
- print("β No version entered. Exiting.")
- return
-
- github_version = f"{base_version}.0"
-
- # Step 1: Update manifest.json on disk to base_version (for Firefox)
- if os.path.exists(manifest_path):
- update_version_line(manifest_path, base_version)
- else:
- print(f"β {TARGET_FILE} not found. Aborting.")
- return
-
- # Step 2: Create videospeed-firefox.xpi (exclude script, .git, AND videospeed-firefox.xpi itself)
- exclude_files = [SCRIPT_NAME, "videospeed-firefox.xpi"]
- exclude_dirs = [".git"]
- zip_folder("videospeed-firefox.xpi", current_dir, exclude_files, exclude_dirs)
- print("β
Created videospeed-firefox.xpi")
-
- # Step 3: Re-scan for .xpi files after Firefox archive creation, exclude them for GitHub zip
- current_xpi_files = set(glob.glob("*.xpi"))
- exclude_temp_files = current_xpi_files.union({SCRIPT_NAME})
- exclude_temp_dirs = set(exclude_dirs)
-
- # Step 4: Create videospeed-github.xpi from temp folder with version bumped to .0
- with tempfile.TemporaryDirectory() as temp_dir:
- for item in os.listdir(current_dir):
- if should_exclude(item, exclude_temp_files, exclude_temp_dirs):
- continue
- src = os.path.join(current_dir, item)
- dst = os.path.join(temp_dir, item)
- if os.path.isdir(src):
- shutil.copytree(src, dst)
- else:
- shutil.copy2(src, dst)
-
- temp_manifest = os.path.join(temp_dir, TARGET_FILE)
- if os.path.exists(temp_manifest):
- update_version_line(temp_manifest, github_version)
- else:
- print(f"β οΈ {TARGET_FILE} not found in temp folder.")
-
- zip_folder(
- "videospeed-github.xpi", temp_dir, exclude_files=[], exclude_dirs=[]
- )
- print("β
Created videospeed-github.xpi")
-
-
-if __name__ == "__main__":
- main()
diff --git a/icons/icon128.png b/extension/assets/icons/icon128.png
similarity index 100%
rename from icons/icon128.png
rename to extension/assets/icons/icon128.png
diff --git a/icons/icon16.png b/extension/assets/icons/icon16.png
similarity index 100%
rename from icons/icon16.png
rename to extension/assets/icons/icon16.png
diff --git a/icons/icon19.png b/extension/assets/icons/icon19.png
similarity index 100%
rename from icons/icon19.png
rename to extension/assets/icons/icon19.png
diff --git a/icons/icon19_disabled.png b/extension/assets/icons/icon19_disabled.png
similarity index 100%
rename from icons/icon19_disabled.png
rename to extension/assets/icons/icon19_disabled.png
diff --git a/icons/icon38.png b/extension/assets/icons/icon38.png
similarity index 100%
rename from icons/icon38.png
rename to extension/assets/icons/icon38.png
diff --git a/icons/icon38_disabled.png b/extension/assets/icons/icon38_disabled.png
similarity index 100%
rename from icons/icon38_disabled.png
rename to extension/assets/icons/icon38_disabled.png
diff --git a/icons/icon48.png b/extension/assets/icons/icon48.png
similarity index 100%
rename from icons/icon48.png
rename to extension/assets/icons/icon48.png
diff --git a/icons/icon48_disabled.png b/extension/assets/icons/icon48_disabled.png
similarity index 100%
rename from icons/icon48_disabled.png
rename to extension/assets/icons/icon48_disabled.png
diff --git a/images/kofi_symbol.svg b/extension/assets/images/kofi_symbol.svg
similarity index 100%
rename from images/kofi_symbol.svg
rename to extension/assets/images/kofi_symbol.svg
diff --git a/background.js b/extension/background/background.js
similarity index 57%
rename from background.js
rename to extension/background/background.js
index 544eb16..7e64d08 100644
--- a/background.js
+++ b/extension/background/background.js
@@ -1,5 +1,5 @@
chrome.runtime.onMessage.addListener(function (request) {
if (request.action === "openOptions") {
- chrome.tabs.create({ url: chrome.runtime.getURL("options.html") });
+ chrome.tabs.create({ url: chrome.runtime.getURL("options/options.html") });
}
});
diff --git a/frameSpeedSnapshot.js b/extension/content/frame-speed-snapshot.js
similarity index 100%
rename from frameSpeedSnapshot.js
rename to extension/content/frame-speed-snapshot.js
diff --git a/inject.css b/extension/content/inject.css
similarity index 100%
rename from inject.css
rename to extension/content/inject.css
diff --git a/inject.js b/extension/content/inject.js
similarity index 99%
rename from inject.js
rename to extension/content/inject.js
index f78c1b4..73c5763 100644
--- a/inject.js
+++ b/extension/content/inject.js
@@ -60,6 +60,7 @@ var YT_NATIVE_MAX = 2.0;
var YT_NATIVE_STEP = 0.05;
var vscObservedRoots = new WeakSet();
var vscConnectedScannedRoots = new WeakSet();
+var vscInitializedDocuments = new Set();
var requestIdle =
typeof window.requestIdleCallback === "function"
? window.requestIdleCallback.bind(window)
@@ -1882,7 +1883,7 @@ function defineVideoController() {
var shadow = wrapper.attachShadow({ mode: "open" });
var shadowStylesheet = doc.createElement("link");
shadowStylesheet.rel = "stylesheet";
- shadowStylesheet.href = chrome.runtime.getURL("shadow.css");
+ shadowStylesheet.href = chrome.runtime.getURL("content/shadow.css");
shadow.appendChild(shadowStylesheet);
var controller = doc.createElement("div");
@@ -2253,8 +2254,6 @@ function setupListener(root) {
root.vscRateListenerAttached = true;
}
-var vscInitializedDocuments = new Set();
-
function clearPendingInitialization(doc) {
if (!doc || !doc.vscPendingInitializeHandler) return;
diff --git a/shadow.css b/extension/content/shadow.css
similarity index 100%
rename from shadow.css
rename to extension/content/shadow.css
diff --git a/manifest.json b/extension/manifest.json
similarity index 73%
rename from manifest.json
rename to extension/manifest.json
index 65e2ccb..9bd4628 100644
--- a/manifest.json
+++ b/extension/manifest.json
@@ -16,13 +16,13 @@
}
},
"icons": {
- "16": "icons/icon16.png",
- "48": "icons/icon48.png",
- "128": "icons/icon128.png"
+ "16": "assets/icons/icon16.png",
+ "48": "assets/icons/icon48.png",
+ "128": "assets/icons/icon128.png"
},
"background": {
"scripts": [
- "background.js"
+ "background/background.js"
]
},
"permissions": [
@@ -30,16 +30,16 @@
"https://cdn.jsdelivr.net/*"
],
"options_ui": {
- "page": "options.html",
+ "page": "options/options.html",
"open_in_tab": true
},
"browser_action": {
"default_icon": {
- "19": "icons/icon19.png",
- "38": "icons/icon38.png",
- "48": "icons/icon48.png"
+ "19": "assets/icons/icon19.png",
+ "38": "assets/icons/icon38.png",
+ "48": "assets/icons/icon48.png"
},
- "default_popup": "popup.html"
+ "default_popup": "popup/popup.html"
},
"content_scripts": [
{
@@ -56,19 +56,19 @@
"https://meet.google.com/*"
],
"css": [
- "inject.css"
+ "content/inject.css"
],
"js": [
"shared/controller-utils.js",
"shared/key-bindings.js",
"shared/site-rules.js",
- "ui-icons.js",
- "inject.js"
+ "shared/ui-icons.js",
+ "content/inject.js"
]
}
],
"web_accessible_resources": [
- "inject.css",
- "shadow.css"
+ "content/inject.css",
+ "content/shadow.css"
]
}
diff --git a/importExport.js b/extension/options/import-export.js
similarity index 100%
rename from importExport.js
rename to extension/options/import-export.js
diff --git a/lucide-client.js b/extension/options/lucide-client.js
similarity index 100%
rename from lucide-client.js
rename to extension/options/lucide-client.js
diff --git a/options.css b/extension/options/options.css
similarity index 100%
rename from options.css
rename to extension/options/options.css
diff --git a/options.html b/extension/options/options.html
similarity index 99%
rename from options.html
rename to extension/options/options.html
index 36367a2..053c157 100644
--- a/options.html
+++ b/extension/options/options.html
@@ -5,14 +5,14 @@
Speeder Settings
-
-
-
-
+
+
+
+
-
-
+
+
@@ -38,7 +38,7 @@
>
Speeder
-
-
-
+
+
+
@@ -66,7 +66,7 @@
aria-label="Support on Ko-fi (opens in new tab)"
>

= 0; i--) {
var attr = el.attributes[i];
var name = attr.name.toLowerCase();
@@ -94,7 +94,7 @@ function vscSanitizeSvgTree(svg) {
}
});
- svg.setAttribute("xmlns", VSC_SVG_NS);
+ svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", VSC_SVG_NS);
svg.setAttribute("aria-hidden", "true");
return svg;
}
diff --git a/scripts/deploy-amo-stable.sh b/scripts/deploy-amo-stable.sh
index eafd495..d70706b 100755
--- a/scripts/deploy-amo-stable.sh
+++ b/scripts/deploy-amo-stable.sh
@@ -7,19 +7,20 @@ set -euo pipefail
ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"
+MANIFEST_PATH="extension/manifest.json"
manifest_version() {
- python3 -c 'import json; print(json.load(open("manifest.json"))["version"])'
+ MANIFEST_PATH="$MANIFEST_PATH" python3 -c 'import json, os; print(json.load(open(os.environ["MANIFEST_PATH"]))["version"])'
}
bump_manifest() {
local ver="$1"
- VER="$ver" python3 <<'PY'
+ VER="$ver" MANIFEST_PATH="$MANIFEST_PATH" python3 <<'PY'
import json
import os
ver = os.environ["VER"]
-path = "manifest.json"
+path = os.environ["MANIFEST_PATH"]
with open(path, encoding="utf-8") as f:
data = json.load(f)
data["version"] = ver
@@ -58,8 +59,8 @@ fi
git checkout beta
git pull origin beta
-echo "Current version on beta (manifest.json): $(manifest_version)"
-read -r -p "Release version for manifest.json + tag (e.g. 5.0.4): " SEMVER_IN
+echo "Current version on beta ($MANIFEST_PATH): $(manifest_version)"
+read -r -p "Release version for $MANIFEST_PATH + tag (e.g. 5.0.4): " SEMVER_IN
SEMVER="$(normalize_semver "$SEMVER_IN")"
validate_semver "$SEMVER"
@@ -73,7 +74,7 @@ fi
echo
echo "This will:"
echo " 1. checkout main, merge --squash origin/beta (single release commit on main)"
-echo " 2. set manifest.json to $SEMVER in that commit (if anything else changed, it is included too)"
+echo " 2. set $MANIFEST_PATH to $SEMVER in that commit (if anything else changed, it is included too)"
echo " 3. push origin main, create tag $TAG, push tag (triggers listed AMO submit)"
echo " 4. checkout dev (merge mainβdev yourself if you want them aligned)"
read -r -p "Continue? [y/N] " confirm
diff --git a/scripts/deploy-beta.sh b/scripts/deploy-beta.sh
index d780d33..3ccd305 100755
--- a/scripts/deploy-beta.sh
+++ b/scripts/deploy-beta.sh
@@ -6,19 +6,20 @@ set -euo pipefail
ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"
+MANIFEST_PATH="extension/manifest.json"
manifest_version() {
- python3 -c 'import json; print(json.load(open("manifest.json"))["version"])'
+ MANIFEST_PATH="$MANIFEST_PATH" python3 -c 'import json, os; print(json.load(open(os.environ["MANIFEST_PATH"]))["version"])'
}
bump_manifest() {
local ver="$1"
- VER="$ver" python3 <<'PY'
+ VER="$ver" MANIFEST_PATH="$MANIFEST_PATH" python3 <<'PY'
import json
import os
ver = os.environ["VER"]
-path = "manifest.json"
+path = os.environ["MANIFEST_PATH"]
with open(path, encoding="utf-8") as f:
data = json.load(f)
data["version"] = ver
@@ -57,8 +58,8 @@ fi
git checkout dev
git pull origin dev
-echo "Current version in manifest.json: $(manifest_version)"
-read -r -p "New version for manifest.json (e.g. 5.0.4): " SEMVER_IN
+echo "Current version in $MANIFEST_PATH: $(manifest_version)"
+read -r -p "New version for $MANIFEST_PATH (e.g. 5.0.4): " SEMVER_IN
SEMVER="$(normalize_semver "$SEMVER_IN")"
validate_semver "$SEMVER"
@@ -76,7 +77,7 @@ fi
echo
echo "This will:"
-echo " 1. set manifest.json version to $SEMVER, commit on dev, push origin dev"
+echo " 1. set $MANIFEST_PATH version to $SEMVER, commit on dev, push origin dev"
echo " 2. checkout beta, merge dev (no-ff), push origin beta"
echo " 3. create tag $TAG and push it (triggers beta AMO + prerelease)"
echo " 4. checkout dev (main is not modified)"
@@ -86,7 +87,7 @@ read -r -p "Continue? [y/N] " confirm
echo "π Releasing beta $TAG"
bump_manifest "$SEMVER"
-git add manifest.json
+git add "$MANIFEST_PATH"
git commit -m "Bump version to $SEMVER"
git push origin dev
diff --git a/tests/helpers/extension-test-utils.js b/tests/helpers/extension-test-utils.js
index fba54f9..9bda6ad 100644
--- a/tests/helpers/extension-test-utils.js
+++ b/tests/helpers/extension-test-utils.js
@@ -1,7 +1,7 @@
const fs = require("fs");
const path = require("path");
const { JSDOM } = require("jsdom");
-const { vi } = require("vitest");
+const vi = globalThis.vi;
const ROOT = path.resolve(__dirname, "..", "..");
@@ -118,7 +118,11 @@ async function flushAsyncWork(turns) {
const count = turns || 2;
for (let i = 0; i < count; i += 1) {
await Promise.resolve();
- await new Promise((resolve) => setTimeout(resolve, 0));
+ if (vi && typeof vi.isFakeTimers === "function" && vi.isFakeTimers()) {
+ await vi.advanceTimersByTimeAsync(0);
+ } else {
+ await new Promise((resolve) => setTimeout(resolve, 0));
+ }
}
}
diff --git a/tests/importExport.integration.test.js b/tests/importExport.integration.test.js
index d1a423a..a692cb9 100644
--- a/tests/importExport.integration.test.js
+++ b/tests/importExport.integration.test.js
@@ -6,14 +6,14 @@ import {
} from "./helpers/browser.js";
async function setupImportExport(overrides = {}) {
- loadHtml("options.html");
+ loadHtml("extension/options/options.html");
globalThis.chrome = createChromeMock(overrides);
window.chrome = globalThis.chrome;
const restoreSpy = vi.fn();
globalThis.restore_options = restoreSpy;
window.restore_options = restoreSpy;
- loadScript("shared/import-export.js");
- loadScript("importExport.js");
+ loadScript("extension/shared/import-export.js");
+ loadScript("extension/options/import-export.js");
await flushAsyncWork();
return globalThis.chrome;
}
diff --git a/tests/importExport.spec.js b/tests/importExport.spec.js
index 6adc500..7cf0dec 100644
--- a/tests/importExport.spec.js
+++ b/tests/importExport.spec.js
@@ -1,8 +1,8 @@
-const { afterEach, beforeEach, describe, expect, it, vi } = require("vitest");
const {
createChromeMock,
evaluateScript,
flushAsyncWork,
+ fireDOMContentLoaded,
installCommonWindowMocks,
loadHtmlString
} = require("./helpers/extension-test-utils");
@@ -26,18 +26,38 @@ function bootImportExport(options) {
global.chrome = chrome;
window.chrome = chrome;
+ class TestBlob {
+ constructor(parts, options) {
+ this.parts = parts;
+ this.options = options;
+ }
+
+ async text() {
+ return this.parts.join("");
+ }
+ }
+ global.Blob = TestBlob;
+ window.Blob = TestBlob;
+
const createObjectURL = vi.fn(() => "blob:test");
const revokeObjectURL = vi.fn();
- vi.stubGlobal("URL", {
- createObjectURL,
- revokeObjectURL
+ Object.defineProperty(window.URL, "createObjectURL", {
+ configurable: true,
+ value: createObjectURL
});
+ Object.defineProperty(window.URL, "revokeObjectURL", {
+ configurable: true,
+ value: revokeObjectURL
+ });
+ global.URL = window.URL;
- evaluateScript("importExport.js");
+ evaluateScript("extension/shared/import-export.js");
+ evaluateScript("extension/options/import-export.js");
+ fireDOMContentLoaded();
return { chrome, createObjectURL, revokeObjectURL };
}
-describe("importExport.js", () => {
+describe("options/import-export.js", () => {
beforeEach(() => {
vi.useFakeTimers();
});
@@ -53,14 +73,15 @@ describe("importExport.js", () => {
bootImportExport();
expect(window.generateBackupFilename()).toBe(
- "speeder-backup_2026-04-04_13.14.15.json"
+ "speeder-backup_2026-04-04_09.14.15.json"
);
});
it("exports sync and local settings into a downloadable backup", async () => {
- const clickSpy = vi
- .spyOn(window.HTMLAnchorElement.prototype, "click")
- .mockImplementation(() => {});
+ Object.defineProperty(window.HTMLAnchorElement.prototype, "click", {
+ configurable: true,
+ value: vi.fn()
+ });
const { createObjectURL, revokeObjectURL } = bootImportExport({
syncData: {
rememberSpeed: true,
@@ -74,7 +95,6 @@ describe("importExport.js", () => {
});
document.querySelector("#exportSettings").click();
- await flushAsyncWork();
expect(createObjectURL).toHaveBeenCalledTimes(1);
const blob = createObjectURL.mock.calls[0][0];
@@ -82,15 +102,15 @@ describe("importExport.js", () => {
expect(backup.settings.rememberSpeed).toBe(true);
expect(backup.localSettings.customButtonIcons.faster.slug).toBe("rocket");
- expect(clickSpy).toHaveBeenCalledTimes(1);
expect(revokeObjectURL).toHaveBeenCalledWith("blob:test");
expect(document.querySelector("#status").textContent).toContain("exported");
});
it("omits Lucide tags cache from exported localSettings", async () => {
- vi.spyOn(window.HTMLAnchorElement.prototype, "click").mockImplementation(
- () => {}
- );
+ Object.defineProperty(window.HTMLAnchorElement.prototype, "click", {
+ configurable: true,
+ value: vi.fn()
+ });
const { createObjectURL } = bootImportExport({
syncData: { rememberSpeed: true },
localData: {
@@ -103,7 +123,6 @@ describe("importExport.js", () => {
});
document.querySelector("#exportSettings").click();
- await flushAsyncWork();
const blob = createObjectURL.mock.calls[0][0];
const backup = JSON.parse(await blob.text());
@@ -159,6 +178,7 @@ describe("importExport.js", () => {
}
vi.stubGlobal("FileReader", FakeFileReader);
+ window.FileReader = FakeFileReader;
document.querySelector("#importSettings").click();
await flushAsyncWork();
@@ -208,6 +228,7 @@ describe("importExport.js", () => {
}
vi.stubGlobal("FileReader", FakeFileReader);
+ window.FileReader = FakeFileReader;
document.querySelector("#importSettings").click();
await flushAsyncWork();
diff --git a/tests/inject.spec.js b/tests/inject.spec.js
index 95006f2..bd70618 100644
--- a/tests/inject.spec.js
+++ b/tests/inject.spec.js
@@ -1,4 +1,3 @@
-const { afterEach, describe, expect, it, vi } = require("vitest");
const {
createChromeMock,
evaluateScript,
@@ -31,8 +30,11 @@ function bootInject(options) {
);
window.cancelIdleCallback = (id) => clearTimeout(id);
- evaluateScript("ui-icons.js");
- evaluateScript("inject.js");
+ evaluateScript("extension/shared/controller-utils.js");
+ evaluateScript("extension/shared/key-bindings.js");
+ evaluateScript("extension/shared/site-rules.js");
+ evaluateScript("extension/shared/ui-icons.js");
+ evaluateScript("extension/content/inject.js");
return chrome;
}
@@ -116,14 +118,14 @@ describe("inject.js helper logic", () => {
bootInject();
await flushAsyncWork(3);
- window.tc.settings.siteRules = [{ pattern: "localhost", enabled: false }];
+ window.tc.settings.siteRules = [{ pattern: "example.org", enabled: false }];
window.captureSiteRuleBase();
expect(window.applySiteRuleOverrides()).toBe(true);
window.resetSettingsFromSiteRuleBase();
window.tc.settings.siteRules = [
{
- pattern: "localhost",
+ pattern: "example.org",
controllerLocation: "bottom-left",
controllerMarginTop: 300,
controllerMarginBottom: -10,
diff --git a/tests/inject.test.js b/tests/inject.test.js
index 34a5110..eb82a02 100644
--- a/tests/inject.test.js
+++ b/tests/inject.test.js
@@ -38,11 +38,11 @@ async function bootInject({ sync = {}, local = {} } = {}) {
);
globalThis.cancelIdleCallback = (id) => clearTimeout(id);
- loadScript("shared/controller-utils.js");
- loadScript("shared/key-bindings.js");
- loadScript("shared/site-rules.js");
- loadScript("ui-icons.js");
- loadScript("inject.js");
+ loadScript("extension/shared/controller-utils.js");
+ loadScript("extension/shared/key-bindings.js");
+ loadScript("extension/shared/site-rules.js");
+ loadScript("extension/shared/ui-icons.js");
+ loadScript("extension/content/inject.js");
for (let i = 0; i < 3; i += 1) {
await flushAsyncWork();
@@ -215,11 +215,11 @@ describe("inject runtime", () => {
Promise.resolve().then(() => originalLocalGet(keys, callback));
});
- loadScript("shared/controller-utils.js");
- loadScript("shared/key-bindings.js");
- loadScript("shared/site-rules.js");
- loadScript("ui-icons.js");
- loadScript("inject.js");
+ loadScript("extension/shared/controller-utils.js");
+ loadScript("extension/shared/key-bindings.js");
+ loadScript("extension/shared/site-rules.js");
+ loadScript("extension/shared/ui-icons.js");
+ loadScript("extension/content/inject.js");
// Fast-forward 3000ms for delayed rescan to trigger
vi.advanceTimersByTime(3000);
@@ -235,4 +235,3 @@ describe("inject runtime", () => {
vi.useRealTimers();
});
});
-
diff --git a/tests/lucide-client.spec.js b/tests/lucide-client.spec.js
index 4b2b050..a209141 100644
--- a/tests/lucide-client.spec.js
+++ b/tests/lucide-client.spec.js
@@ -1,4 +1,3 @@
-const { afterEach, describe, expect, it } = require("vitest");
const {
evaluateScript,
loadHtmlString
@@ -11,8 +10,8 @@ describe("lucide-client.js", () => {
it("builds icon URLs and rejects invalid slugs", () => {
loadHtmlString("");
- evaluateScript("ui-icons.js");
- evaluateScript("lucide-client.js");
+ evaluateScript("extension/shared/ui-icons.js");
+ evaluateScript("extension/options/lucide-client.js");
expect(window.lucideIconSvgUrl("alarm-clock")).toContain(
"/icons/alarm-clock.svg"
@@ -23,8 +22,8 @@ describe("lucide-client.js", () => {
it("sanitizes SVG before persisting a Lucide icon", () => {
loadHtmlString("");
- evaluateScript("ui-icons.js");
- evaluateScript("lucide-client.js");
+ evaluateScript("extension/shared/ui-icons.js");
+ evaluateScript("extension/options/lucide-client.js");
const sanitized = window.sanitizeLucideSvg(`