mirror of
https://github.com/SoPat712/Speeder.git
synced 2026-10-08 03:03:14 -04:00
Reorganize extension source layout
This commit is contained in:
47 files changed
+198
-315
No files matched your search
@@ -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
|
||||
@@ -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?
|
||||
|
||||
@@ -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()
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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") });
|
||||
}
|
||||
});
|
||||
File renamed without changes.
File renamed without changes.
@@ -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;
|
||||
|
||||
File renamed without changes.
@@ -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"
|
||||
]
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -5,14 +5,14 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Speeder Settings</title>
|
||||
<link rel="stylesheet" href="options.css" />
|
||||
<script src="shared/controller-utils.js"></script>
|
||||
<script src="shared/key-bindings.js"></script>
|
||||
<script src="shared/popup-controls.js"></script>
|
||||
<script src="ui-icons.js"></script>
|
||||
<script src="../shared/controller-utils.js"></script>
|
||||
<script src="../shared/key-bindings.js"></script>
|
||||
<script src="../shared/popup-controls.js"></script>
|
||||
<script src="../shared/ui-icons.js"></script>
|
||||
<script src="lucide-client.js"></script>
|
||||
<script src="options.js"></script>
|
||||
<script src="shared/import-export.js"></script>
|
||||
<script src="importExport.js"></script>
|
||||
<script src="../shared/import-export.js"></script>
|
||||
<script src="import-export.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-shell">
|
||||
@@ -38,7 +38,7 @@
|
||||
>
|
||||
<img
|
||||
class="support-cta-kofi-img"
|
||||
src="images/kofi_symbol.svg"
|
||||
src="../assets/images/kofi_symbol.svg"
|
||||
width="241"
|
||||
height="194"
|
||||
alt=""
|
||||
File renamed without changes.
File renamed without changes.
@@ -4,9 +4,9 @@
|
||||
<meta charset="utf-8" />
|
||||
<title>Speeder</title>
|
||||
<link rel="stylesheet" href="popup.css" />
|
||||
<script src="shared/site-rules.js"></script>
|
||||
<script src="shared/popup-controls.js"></script>
|
||||
<script src="ui-icons.js"></script>
|
||||
<script src="../shared/site-rules.js"></script>
|
||||
<script src="../shared/popup-controls.js"></script>
|
||||
<script src="../shared/ui-icons.js"></script>
|
||||
<script src="popup.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -66,7 +66,7 @@
|
||||
aria-label="Support on Ko-fi (opens in new tab)"
|
||||
>
|
||||
<img
|
||||
src="images/kofi_symbol.svg"
|
||||
src="../assets/images/kofi_symbol.svg"
|
||||
width="28"
|
||||
height="22"
|
||||
alt=""
|
||||
@@ -4,7 +4,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
var siteRuleUtils = speederShared.siteRules || {};
|
||||
var popupControlUtils = speederShared.popupControls || {};
|
||||
|
||||
/* `label` is only used if ui-icons.js has no path for this action (fallback). */
|
||||
/* `label` is only used if shared/ui-icons.js has no path for this action (fallback). */
|
||||
var controllerButtonDefs = {
|
||||
rewind: { label: "", className: "rw" },
|
||||
slower: { label: "", className: "" },
|
||||
@@ -128,7 +128,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
var tabId = tabs[0].id;
|
||||
chrome.tabs.executeScript(
|
||||
tabId,
|
||||
{ allFrames: true, file: "frameSpeedSnapshot.js" },
|
||||
{ allFrames: true, file: "content/frame-speed-snapshot.js" },
|
||||
function (results) {
|
||||
if (chrome.runtime.lastError) {
|
||||
sendToActiveTab({ action: "get_speed" }, function (response) {
|
||||
@@ -192,7 +192,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
btn.addEventListener("click", function () {
|
||||
if (btnId === "settings") {
|
||||
window.open(chrome.runtime.getURL("options.html"));
|
||||
window.open(chrome.runtime.getURL("options/options.html"));
|
||||
return;
|
||||
}
|
||||
sendToActiveTab(
|
||||
@@ -210,11 +210,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
var manifest = chrome.runtime.getManifest();
|
||||
var versionElement = document.querySelector("#app-version");
|
||||
if (versionElement) {
|
||||
versionElement.innerText = manifest.version;
|
||||
versionElement.textContent = manifest.version;
|
||||
}
|
||||
|
||||
document.querySelector("#config").addEventListener("click", function () {
|
||||
window.open(chrome.runtime.getURL("options.html"));
|
||||
window.open(chrome.runtime.getURL("options/options.html"));
|
||||
});
|
||||
|
||||
document.querySelector("#about").addEventListener("click", function () {
|
||||
@@ -350,9 +350,9 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
const suffix = `${enabled ? "" : "_disabled"}.png`;
|
||||
chrome.browserAction.setIcon({
|
||||
path: {
|
||||
19: "icons/icon19" + suffix,
|
||||
38: "icons/icon38" + suffix,
|
||||
48: "icons/icon48" + suffix
|
||||
19: "assets/icons/icon19" + suffix,
|
||||
38: "assets/icons/icon38" + suffix,
|
||||
48: "assets/icons/icon48" + suffix
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -366,12 +366,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
function setStatusMessage(str) {
|
||||
const status_element = document.querySelector("#status");
|
||||
status_element.classList.toggle("hide", false);
|
||||
status_element.innerText = str;
|
||||
status_element.textContent = str;
|
||||
}
|
||||
|
||||
function clearStatusMessage() {
|
||||
const status_element = document.querySelector("#status");
|
||||
status_element.classList.toggle("hide", true);
|
||||
status_element.innerText = "";
|
||||
status_element.textContent = "";
|
||||
}
|
||||
});
|
||||
File renamed without changes.
@@ -50,7 +50,7 @@
|
||||
/**
|
||||
* Local-only keys excluded from backup JSON. These are disposable caches
|
||||
* (e.g. Lucide tags.json) that bloat exports and are refetched when needed.
|
||||
* Keep in sync with lucide-client.js (LUCIDE_TAGS_CACHE_KEY + "At").
|
||||
* Keep in sync with options/lucide-client.js (LUCIDE_TAGS_CACHE_KEY + "At").
|
||||
*/
|
||||
var localSettingsKeysOmittedFromExport = [
|
||||
"lucideTagsCacheV1",
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -76,7 +76,7 @@ function vscSanitizeSvgTree(svg) {
|
||||
n.remove();
|
||||
});
|
||||
|
||||
svg.querySelectorAll("*").forEach(function (el) {
|
||||
[svg].concat(Array.from(svg.querySelectorAll("*"))).forEach(function (el) {
|
||||
for (var i = el.attributes.length - 1; i >= 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,8 +118,12 @@ async function flushAsyncWork(turns) {
|
||||
const count = turns || 2;
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
await Promise.resolve();
|
||||
if (vi && typeof vi.isFakeTimers === "function" && vi.isFakeTimers()) {
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
} else {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pickStorageValues(data, keys) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+37
-16
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
+10
-11
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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("<!doctype html><html><body></body></html>");
|
||||
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("<!doctype html><html><body></body></html>");
|
||||
evaluateScript("ui-icons.js");
|
||||
evaluateScript("lucide-client.js");
|
||||
evaluateScript("extension/shared/ui-icons.js");
|
||||
evaluateScript("extension/options/lucide-client.js");
|
||||
|
||||
const sanitized = window.sanitizeLucideSvg(`
|
||||
<svg width="10" height="10" onclick="evil()">
|
||||
@@ -43,8 +42,8 @@ describe("lucide-client.js", () => {
|
||||
|
||||
it("searches and ranks icon slugs by query", () => {
|
||||
loadHtmlString("<!doctype html><html><body></body></html>");
|
||||
evaluateScript("ui-icons.js");
|
||||
evaluateScript("lucide-client.js");
|
||||
evaluateScript("extension/shared/ui-icons.js");
|
||||
evaluateScript("extension/options/lucide-client.js");
|
||||
|
||||
const results = window.searchLucideSlugs(
|
||||
{
|
||||
|
||||
@@ -7,16 +7,16 @@ import {
|
||||
} from "./helpers/browser.js";
|
||||
|
||||
async function setupOptions(overrides = {}) {
|
||||
loadHtml("options.html");
|
||||
loadHtml("extension/options/options.html");
|
||||
globalThis.chrome = createChromeMock(overrides);
|
||||
window.chrome = globalThis.chrome;
|
||||
globalThis.fetch = vi.fn();
|
||||
loadScript("shared/controller-utils.js");
|
||||
loadScript("shared/key-bindings.js");
|
||||
loadScript("shared/popup-controls.js");
|
||||
loadScript("ui-icons.js");
|
||||
loadScript("lucide-client.js");
|
||||
loadScript("options.js");
|
||||
loadScript("extension/shared/controller-utils.js");
|
||||
loadScript("extension/shared/key-bindings.js");
|
||||
loadScript("extension/shared/popup-controls.js");
|
||||
loadScript("extension/shared/ui-icons.js");
|
||||
loadScript("extension/options/lucide-client.js");
|
||||
loadScript("extension/options/options.js");
|
||||
triggerDomContentLoaded();
|
||||
await flushAsyncWork();
|
||||
return globalThis.chrome;
|
||||
|
||||
+16
-10
@@ -1,4 +1,3 @@
|
||||
const { afterEach, beforeEach, describe, expect, it, vi } = require("vitest");
|
||||
const {
|
||||
createChromeMock,
|
||||
evaluateScript,
|
||||
@@ -11,7 +10,7 @@ const {
|
||||
function bootOptions(options) {
|
||||
const config = options || {};
|
||||
|
||||
loadHtmlFile("options.html");
|
||||
loadHtmlFile("extension/options/options.html");
|
||||
installCommonWindowMocks();
|
||||
|
||||
const chrome = createChromeMock({
|
||||
@@ -31,9 +30,12 @@ function bootOptions(options) {
|
||||
);
|
||||
window.fetch = global.fetch;
|
||||
|
||||
evaluateScript("ui-icons.js");
|
||||
evaluateScript("lucide-client.js");
|
||||
evaluateScript("options.js");
|
||||
evaluateScript("extension/shared/controller-utils.js");
|
||||
evaluateScript("extension/shared/key-bindings.js");
|
||||
evaluateScript("extension/shared/popup-controls.js");
|
||||
evaluateScript("extension/shared/ui-icons.js");
|
||||
evaluateScript("extension/options/lucide-client.js");
|
||||
evaluateScript("extension/options/options.js");
|
||||
fireDOMContentLoaded();
|
||||
|
||||
return chrome;
|
||||
@@ -119,7 +121,8 @@ describe("options.js", () => {
|
||||
window.populatePopupControlBarEditor(["advance", "settings", "rewind"]);
|
||||
|
||||
window.createSiteRule({ pattern: "youtube.com" });
|
||||
const ruleEl = document.querySelector(".site-rule");
|
||||
const siteRuleEls = document.querySelectorAll(".site-rule");
|
||||
const ruleEl = siteRuleEls[siteRuleEls.length - 1];
|
||||
ruleEl.querySelector(".override-placement").checked = true;
|
||||
ruleEl.querySelector(".site-controllerLocation").value = "top-right";
|
||||
ruleEl.querySelector(".site-controllerMarginTop").value = "300";
|
||||
@@ -167,8 +170,10 @@ describe("options.js", () => {
|
||||
expect(savedSettings.controllerMarginTop).toBe(200);
|
||||
expect(savedSettings.controllerMarginBottom).toBe(0);
|
||||
expect(savedSettings.popupControllerButtons).toEqual(["advance", "rewind"]);
|
||||
expect(savedSettings.siteRules).toEqual([
|
||||
{
|
||||
expect(savedSettings.siteRules).toHaveLength(3);
|
||||
expect(savedSettings.siteRules).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
pattern: "youtube.com",
|
||||
enabled: true,
|
||||
controllerLocation: "top-right",
|
||||
@@ -178,8 +183,9 @@ describe("options.js", () => {
|
||||
hideWithControlsTimer: 0.1,
|
||||
showPopupControlBar: false,
|
||||
popupControllerButtons: ["advance", "rewind"]
|
||||
}
|
||||
]);
|
||||
})
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it("blocks save when a site rule regex is invalid", async () => {
|
||||
|
||||
@@ -7,13 +7,13 @@ import {
|
||||
} from "./helpers/browser.js";
|
||||
|
||||
async function setupPopup(overrides = {}) {
|
||||
loadHtml("popup.html");
|
||||
loadHtml("extension/popup/popup.html");
|
||||
globalThis.chrome = createChromeMock(overrides);
|
||||
window.chrome = globalThis.chrome;
|
||||
loadScript("shared/site-rules.js");
|
||||
loadScript("shared/popup-controls.js");
|
||||
loadScript("ui-icons.js");
|
||||
loadScript("popup.js");
|
||||
loadScript("extension/shared/site-rules.js");
|
||||
loadScript("extension/shared/popup-controls.js");
|
||||
loadScript("extension/shared/ui-icons.js");
|
||||
loadScript("extension/popup/popup.js");
|
||||
triggerDomContentLoaded();
|
||||
await flushAsyncWork();
|
||||
return globalThis.chrome;
|
||||
@@ -29,7 +29,7 @@ describe("popup UI", () => {
|
||||
]
|
||||
});
|
||||
|
||||
expect(document.getElementById("app-version").innerText).toBe("5.1.7.0");
|
||||
expect(document.getElementById("app-version").textContent).toBe("5.1.7.0");
|
||||
expect(document.getElementById("popupSpeed").textContent).toBe("1.75");
|
||||
expect(
|
||||
document.querySelectorAll("#popupControlBar button").length
|
||||
@@ -60,7 +60,7 @@ describe("popup UI", () => {
|
||||
}
|
||||
});
|
||||
|
||||
expect(document.getElementById("status").innerText).toBe(
|
||||
expect(document.getElementById("status").textContent).toBe(
|
||||
"Speeder is disabled for this site."
|
||||
);
|
||||
expect(document.getElementById("popupSpeed").textContent).toBe("1.00");
|
||||
@@ -85,9 +85,9 @@ describe("popup UI", () => {
|
||||
);
|
||||
expect(chrome.browserAction.setIcon).toHaveBeenCalledWith({
|
||||
path: {
|
||||
19: "icons/icon19_disabled.png",
|
||||
38: "icons/icon38_disabled.png",
|
||||
48: "icons/icon48_disabled.png"
|
||||
19: "assets/icons/icon19_disabled.png",
|
||||
38: "assets/icons/icon38_disabled.png",
|
||||
48: "assets/icons/icon48_disabled.png"
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -105,13 +105,13 @@ describe("popup UI", () => {
|
||||
});
|
||||
|
||||
document.getElementById("refresh").click();
|
||||
expect(document.getElementById("status").innerText).toBe(
|
||||
expect(document.getElementById("status").textContent).toBe(
|
||||
"Cannot run on this page."
|
||||
);
|
||||
|
||||
response = { status: "complete" };
|
||||
document.getElementById("refresh").click();
|
||||
expect(document.getElementById("status").innerText).toBe(
|
||||
expect(document.getElementById("status").textContent).toBe(
|
||||
"Scan complete. Closing..."
|
||||
);
|
||||
vi.advanceTimersByTime(500);
|
||||
|
||||
+20
-20
@@ -1,4 +1,3 @@
|
||||
const { afterEach, beforeEach, describe, expect, it, vi } = require("vitest");
|
||||
const {
|
||||
createChromeMock,
|
||||
evaluateScript,
|
||||
@@ -11,7 +10,7 @@ const {
|
||||
function bootPopup(options) {
|
||||
const config = options || {};
|
||||
|
||||
loadHtmlFile("popup.html");
|
||||
loadHtmlFile("extension/popup/popup.html");
|
||||
installCommonWindowMocks();
|
||||
|
||||
const chrome = createChromeMock({
|
||||
@@ -47,8 +46,10 @@ function bootPopup(options) {
|
||||
global.chrome = chrome;
|
||||
window.chrome = chrome;
|
||||
|
||||
evaluateScript("ui-icons.js");
|
||||
evaluateScript("popup.js");
|
||||
evaluateScript("extension/shared/site-rules.js");
|
||||
evaluateScript("extension/shared/popup-controls.js");
|
||||
evaluateScript("extension/shared/ui-icons.js");
|
||||
evaluateScript("extension/popup/popup.js");
|
||||
fireDOMContentLoaded();
|
||||
|
||||
return chrome;
|
||||
@@ -90,23 +91,24 @@ describe("popup.js", () => {
|
||||
});
|
||||
|
||||
it("builds sanitized popup buttons and refreshes speed after an action", async () => {
|
||||
let speedQueryCount = 0;
|
||||
const chrome = bootPopup({
|
||||
syncData: {
|
||||
enabled: true,
|
||||
controllerButtons: ["faster", "settings", "rewind", "faster"],
|
||||
popupMatchHoverControls: true
|
||||
}
|
||||
});
|
||||
|
||||
chrome.tabs.executeScript
|
||||
.mockImplementationOnce((tabId, details, callback) => {
|
||||
callback([
|
||||
},
|
||||
executeScriptImpl: (tabId, details, callback) => {
|
||||
speedQueryCount += 1;
|
||||
callback(
|
||||
speedQueryCount <= 2
|
||||
? [
|
||||
{ speed: 1.25, preferred: false },
|
||||
{ speed: 1.5, preferred: true }
|
||||
]);
|
||||
})
|
||||
.mockImplementationOnce((tabId, details, callback) => {
|
||||
callback([{ speed: 1.75, preferred: true }]);
|
||||
]
|
||||
: [{ speed: 1.75, preferred: true }]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
chrome.tabs.sendMessage.mockImplementation((tabId, message, callback) => {
|
||||
@@ -117,7 +119,6 @@ describe("popup.js", () => {
|
||||
callback({ speed: 1.0 });
|
||||
});
|
||||
|
||||
document.dispatchEvent(new window.Event("DOMContentLoaded"));
|
||||
await flushAsyncWork();
|
||||
|
||||
const buttons = Array.from(
|
||||
@@ -138,8 +139,6 @@ describe("popup.js", () => {
|
||||
});
|
||||
|
||||
it("toggles enablement and closes after a successful refresh", async () => {
|
||||
vi.useFakeTimers();
|
||||
|
||||
const chrome = bootPopup({
|
||||
syncData: {
|
||||
enabled: false
|
||||
@@ -147,6 +146,7 @@ describe("popup.js", () => {
|
||||
});
|
||||
|
||||
await flushAsyncWork();
|
||||
vi.useFakeTimers();
|
||||
|
||||
expect(document.querySelector("#enable").classList.contains("hide")).toBe(false);
|
||||
expect(document.querySelector("#disable").classList.contains("hide")).toBe(true);
|
||||
@@ -158,9 +158,9 @@ describe("popup.js", () => {
|
||||
);
|
||||
expect(chrome.browserAction.setIcon).toHaveBeenCalledWith({
|
||||
path: {
|
||||
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"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import controllerUtils from "../shared/controller-utils.js";
|
||||
import importExportUtils from "../shared/import-export.js";
|
||||
import keyBindingUtils from "../shared/key-bindings.js";
|
||||
import popupControls from "../shared/popup-controls.js";
|
||||
import siteRules from "../shared/site-rules.js";
|
||||
import controllerUtils from "../extension/shared/controller-utils.js";
|
||||
import importExportUtils from "../extension/shared/import-export.js";
|
||||
import keyBindingUtils from "../extension/shared/key-bindings.js";
|
||||
import popupControls from "../extension/shared/popup-controls.js";
|
||||
import siteRules from "../extension/shared/site-rules.js";
|
||||
|
||||
describe("shared helpers", () => {
|
||||
it("matches site rules and skips invalid regex patterns", () => {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ module.exports = defineConfig({
|
||||
clearMocks: true,
|
||||
globals: true,
|
||||
restoreMocks: true,
|
||||
include: ["tests/**/*.test.js"],
|
||||
include: ["tests/**/*.test.js", "tests/**/*.spec.js"],
|
||||
setupFiles: ["./tests/setup.js"]
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user