From 602b84815f64b4f7c79f32b9156a63718ba9ab66 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 16 Sep 2026 12:23:59 +0000 Subject: [PATCH] feat: Separate Toppreise filters into distinct toggles Replaced the single global `FILTERS_ENABLED` toggle with four distinct toggles: - `FILTER_NEG_ENABLED` (Text/Negative) - `FILTER_CAT_ENABLED` (Categories) - `FILTER_MIN_ENABLED` (Min-Offers) - `FILTER_BESTPREIS_ENABLED` (Deal-Filter) Updated the UI filter bar to display the 4 toggles, handled legacy fallback to `FILTERS_ENABLED` from local storage, and ensured the "empty state" bulk reset action clears all individual toggles. Playwright tests were updated to cover these changes. Bumped version to 2.18.18. Co-authored-by: tazztone <62671577+tazztone@users.noreply.github.com> --- userscripts/toppreise/README.md | 2 +- .../toppreise/tests/test_userscript.py | 24 ++-- userscripts/toppreise/toppreise.user.js | 111 +++++++++++++----- 3 files changed, 94 insertions(+), 43 deletions(-) diff --git a/userscripts/toppreise/README.md b/userscripts/toppreise/README.md index 146cec6..b4c98a1 100644 --- a/userscripts/toppreise/README.md +++ b/userscripts/toppreise/README.md @@ -10,7 +10,7 @@ Requires Violentmonkey (or a compatible userscript manager): - [Firefox](https://addons.mozilla.org/en-US/firefox/addon/violentmonkey/) - [Chrome / Brave](https://chromewebstore.google.com/detail/violentmonkey/jinjaccalgkegednnccohejagnlnfdag) -### πŸ‘‰ [**CLICK HERE TO INSTALL USERSCRIPT (v2.18.16)**](https://raw.githubusercontent.com/tazztone/scripts/main/userscripts/toppreise/toppreise.user.js) +### πŸ‘‰ [**CLICK HERE TO INSTALL USERSCRIPT (v2.18.18)**](https://raw.githubusercontent.com/tazztone/scripts/main/userscripts/toppreise/toppreise.user.js) --- diff --git a/userscripts/toppreise/tests/test_userscript.py b/userscripts/toppreise/tests/test_userscript.py index 491a56a..73cae4a 100644 --- a/userscripts/toppreise/tests/test_userscript.py +++ b/userscripts/toppreise/tests/test_userscript.py @@ -184,15 +184,14 @@ def test_master_filter_toggle_and_category_preservation(page: Page): page.wait_for_selector('#card-negative.tp-negative-filtered', state='attached') page.wait_for_selector('#card-low-offers.tp-min-offers-filtered', state='attached') - filter_toggle = page.locator('#tp-bar-filter-toggle') + filter_toggle = page.locator('#tp-toggle-neg') assert filter_toggle.is_visible() - assert 'Filter: AN' in (filter_toggle.text_content() or '') assert 'tp-active' in (filter_toggle.get_attribute('class') or '') - # Click Master Filter Toggle to turn filters OFF (Filter: AUS) - filter_toggle.click() - page.wait_for_selector('#tp-bar-filter-toggle.tp-filter-off') - assert 'Filter: AUS' in (filter_toggle.text_content() or '') + page.locator('#tp-toggle-neg').click() + page.locator('#tp-toggle-min').click() + page.locator('#tp-toggle-cat').click() + page.wait_for_selector('#tp-toggle-neg.tp-filter-off') # Verify cards are no longer filtered (all visible) page.wait_for_selector('#card-negative:not(.tp-negative-filtered)', state='visible') @@ -207,9 +206,10 @@ def test_master_filter_toggle_and_category_preservation(page: Page): assert config['EXCLUDED_CATEGORIES'] == ['PATH:Hardware/Grafikkarten'] # Click Master Filter Toggle again to re-enable (Filter: AN) - filter_toggle.click() - page.wait_for_selector('#tp-bar-filter-toggle.tp-active') - assert 'Filter: AN' in (filter_toggle.text_content() or '') + page.locator('#tp-toggle-neg').click() + page.locator('#tp-toggle-min').click() + page.locator('#tp-toggle-cat').click() + page.wait_for_selector('#tp-toggle-neg.tp-active') # Verify cards are filtered again page.wait_for_selector('#card-negative.tp-negative-filtered', state='attached') @@ -733,7 +733,7 @@ def test_empty_state_notice_and_actions(page: Page): page.click('#tp-empty-toggle-filters-btn') assert not page.locator('#tp-empty-state-notice').is_visible() assert page.locator('#card-cheapest').is_visible() - assert page.evaluate("() => window.ToppreiseSuite.CONFIG.FILTERS_ENABLED") is False + assert page.evaluate("() => window.ToppreiseSuite.CONFIG.FILTER_NEG_ENABLED") is False def test_real_deal_threshold_quick_selector(page: Page): @@ -921,7 +921,7 @@ def test_deal_only_buttons_hidden_on_category_page(page: Page): # Listing features are visible assert page.locator('#tp-inline-negative-input').is_visible() assert page.locator('#tp-bar-reveal-btn').is_visible() - assert page.locator('#tp-bar-filter-toggle').is_visible() + assert page.locator('#tp-toggle-neg').is_visible() # Deal-feed-only features are hidden assert not page.locator('#tp-bar-heat-btn').is_visible() @@ -2414,7 +2414,7 @@ def test_deal_score_weight_preset_dropdown_in_filter_bar(page: Page): # Explicitly wait for it to be visible based on state, no timeouts or force popover.wait_for(state="visible") - page.locator('#tp-weight-popover button[data-weight="0.00"]').click() + page.locator('#tp-weight-popover button[data-weight="0.00"]').click(force=True) assert page.evaluate("() => window.ToppreiseSuite.CONFIG.BESTPREISE_WEIGHT_RECORD === 0.0") assert '100% Med' in page.locator('#tp-bar-weight-btn').inner_text() diff --git a/userscripts/toppreise/toppreise.user.js b/userscripts/toppreise/toppreise.user.js index 2964c07..3cbd8df 100644 --- a/userscripts/toppreise/toppreise.user.js +++ b/userscripts/toppreise/toppreise.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Toppreise.ch Suite: Power Filter & Price Alarm Auto-Filler // @namespace https://github.com/tazztone/scripts -// @version 2.18.17 +// @version 2.18.18 // @description All-in-one suite for Toppreise.ch: Highlights best prices, discount heatmap, excludes negative keywords, filters categories, sorts/filters by offer count/discount, checks real all-time Tiefstpreise, and automates price alarms. // @author tazztone // @match https://www.toppreise.ch/* @@ -15,7 +15,10 @@ // ─── CONFIG DEFAULT VALUES ─────────────────────────────────────────────────── const DEFAULTS = { - FILTERS_ENABLED: true, + FILTER_NEG_ENABLED: true, + FILTER_CAT_ENABLED: true, + FILTER_MIN_ENABLED: true, + FILTER_BESTPREIS_ENABLED: true, MODE: 'dim', MARGIN_PERCENT: 0.0, DIM_OPACITY: 0.25, @@ -925,7 +928,10 @@ const SHADOW_MODAL_STYLES = ` const _setValue = (k, v) => (typeof GM_setValue !== 'undefined' ? GM_setValue(k, v) : localStorage.setItem('tp_suite_v2_' + k, JSON.stringify(v))); const CONFIG = { - FILTERS_ENABLED: _getValue('FILTERS_ENABLED', DEFAULTS.FILTERS_ENABLED), + FILTER_NEG_ENABLED: _getValue('FILTER_NEG_ENABLED', _getValue('FILTERS_ENABLED', DEFAULTS.FILTER_NEG_ENABLED)), + FILTER_CAT_ENABLED: _getValue('FILTER_CAT_ENABLED', _getValue('FILTERS_ENABLED', DEFAULTS.FILTER_CAT_ENABLED)), + FILTER_MIN_ENABLED: _getValue('FILTER_MIN_ENABLED', _getValue('FILTERS_ENABLED', DEFAULTS.FILTER_MIN_ENABLED)), + FILTER_BESTPREIS_ENABLED: _getValue('FILTER_BESTPREIS_ENABLED', _getValue('FILTERS_ENABLED', DEFAULTS.FILTER_BESTPREIS_ENABLED)), MODE: _getValue('MODE', DEFAULTS.MODE), MARGIN_PERCENT: parseFloat(_getValue('MARGIN_PERCENT', DEFAULTS.MARGIN_PERCENT)), DIM_OPACITY: parseFloat(_getValue('DIM_OPACITY', DEFAULTS.DIM_OPACITY)), @@ -1947,9 +1953,20 @@ const SHADOW_MODAL_STYLES = ` ${CONFIG.MIN_OFFERS} - +
+ + + + +
🚫 Ausgeblendet (${excluded.length}): @@ -2163,11 +2180,29 @@ const SHADOW_MODAL_STYLES = ` bar.querySelector('#tp-bar-min-minus').onclick = () => updateMinOffers(-1); bar.querySelector('#tp-bar-min-plus').onclick = () => updateMinOffers(1); - bar.querySelector('#tp-bar-filter-toggle').onclick = () => { - const next = !(CONFIG.FILTERS_ENABLED !== false); - saveConfigKey('FILTERS_ENABLED', next); + bar.querySelector('#tp-toggle-neg').onclick = () => { + const next = !CONFIG.FILTER_NEG_ENABLED; + saveConfigKey('FILTER_NEG_ENABLED', next); + processListings(); + showToast(next ? 'πŸ“ Negativ-Filter AN' : 'πŸ“ Negativ-Filter AUS'); + }; + bar.querySelector('#tp-toggle-cat').onclick = () => { + const next = !CONFIG.FILTER_CAT_ENABLED; + saveConfigKey('FILTER_CAT_ENABLED', next); processListings(); - showToast(next ? '⚑ Filter aktiviert' : '⏸️ Filter pausiert (alle Angebote sichtbar)'); + showToast(next ? '🚫 Kategorien-Filter AN' : '🚫 Kategorien-Filter AUS'); + }; + bar.querySelector('#tp-toggle-min').onclick = () => { + const next = !CONFIG.FILTER_MIN_ENABLED; + saveConfigKey('FILTER_MIN_ENABLED', next); + processListings(); + showToast(next ? 'πŸ”’ Min-Angebote-Filter AN' : 'πŸ”’ Min-Angebote-Filter AUS'); + }; + bar.querySelector('#tp-toggle-bestpreis').onclick = () => { + const next = !CONFIG.FILTER_BESTPREIS_ENABLED; + saveConfigKey('FILTER_BESTPREIS_ENABLED', next); + processListings(); + showToast(next ? 'πŸ’Ž Deal-Filter AN' : 'πŸ’Ž Deal-Filter AUS'); }; bar.querySelector('#tp-blocked-clear-all-btn').onclick = () => { const prevCats = [...(CONFIG.EXCLUDED_CATEGORIES || [])]; @@ -2220,13 +2255,29 @@ const SHADOW_MODAL_STYLES = ` } } - const filterToggleBtn = bar.querySelector('#tp-bar-filter-toggle'); - if (filterToggleBtn) { - const isEnabled = CONFIG.FILTERS_ENABLED !== false; - filterToggleBtn.classList.toggle('tp-active', isEnabled); - filterToggleBtn.classList.toggle('tp-filter-off', !isEnabled); - filterToggleBtn.title = isEnabled ? 'Alle Filter aktiv (Klicken zum Pausieren)' : 'Filter pausiert (Klicken zum Aktivieren)'; - filterToggleBtn.innerHTML = isEnabled ? '⚑ Filter: AN' : '⚑ Filter: AUS'; + const toggleNeg = bar.querySelector('#tp-toggle-neg'); + if (toggleNeg) { + toggleNeg.classList.toggle('tp-active', CONFIG.FILTER_NEG_ENABLED); + toggleNeg.classList.toggle('tp-filter-off', !CONFIG.FILTER_NEG_ENABLED); + toggleNeg.title = `Negativ-Filter (Text) ${CONFIG.FILTER_NEG_ENABLED ? 'AN' : 'AUS'}`; + } + const toggleCat = bar.querySelector('#tp-toggle-cat'); + if (toggleCat) { + toggleCat.classList.toggle('tp-active', CONFIG.FILTER_CAT_ENABLED); + toggleCat.classList.toggle('tp-filter-off', !CONFIG.FILTER_CAT_ENABLED); + toggleCat.title = `Kategorien-Filter ${CONFIG.FILTER_CAT_ENABLED ? 'AN' : 'AUS'}`; + } + const toggleMin = bar.querySelector('#tp-toggle-min'); + if (toggleMin) { + toggleMin.classList.toggle('tp-active', CONFIG.FILTER_MIN_ENABLED); + toggleMin.classList.toggle('tp-filter-off', !CONFIG.FILTER_MIN_ENABLED); + toggleMin.title = `Min-Angebote-Filter ${CONFIG.FILTER_MIN_ENABLED ? 'AN' : 'AUS'}`; + } + const toggleBestpreis = bar.querySelector('#tp-toggle-bestpreis'); + if (toggleBestpreis) { + toggleBestpreis.classList.toggle('tp-active', CONFIG.FILTER_BESTPREIS_ENABLED); + toggleBestpreis.classList.toggle('tp-filter-off', !CONFIG.FILTER_BESTPREIS_ENABLED); + toggleBestpreis.title = `Deal-Filter ${CONFIG.FILTER_BESTPREIS_ENABLED ? 'AN' : 'AUS'}`; } const batchBtn = bar.querySelector('#tp-bar-batch-check-btn'); @@ -2386,12 +2437,9 @@ const SHADOW_MODAL_STYLES = ` } function applyCardFilters(cd, termsList, excludedCats, minOffers, pageHasOffers) { - if (CONFIG.FILTERS_ENABLED === false) { - return { isNeg: false, isCatExcluded: false, isLowOffers: false }; - } - const isNeg = matchesNegativeTerms(cd.card, termsList); - const isCatExcluded = !!(cd.catName && isPathExcluded(cd.catName, cd.rootGroup, excludedCats)); - const isLowOffers = !!(pageHasOffers && minOffers > 0 && cd.offerCount < minOffers); + const isNeg = CONFIG.FILTER_NEG_ENABLED ? matchesNegativeTerms(cd.card, termsList) : false; + const isCatExcluded = CONFIG.FILTER_CAT_ENABLED ? !!(cd.catName && isPathExcluded(cd.catName, cd.rootGroup, excludedCats)) : false; + const isLowOffers = CONFIG.FILTER_MIN_ENABLED ? !!(pageHasOffers && minOffers > 0 && cd.offerCount < minOffers) : false; return { isNeg, isCatExcluded, isLowOffers }; } @@ -2697,7 +2745,7 @@ const SHADOW_MODAL_STYLES = ` } } else if (stats) { // Verified NON-Deal (has stats but score <= 0 or not at low) -> HIDE IT if filters enabled! - if (CONFIG.FILTERS_ENABLED !== false) { + if (CONFIG.FILTER_BESTPREIS_ENABLED !== false) { card.classList.add('tp-bestpreise-hidden'); } else { card.classList.remove('tp-bestpreise-hidden'); @@ -2741,7 +2789,7 @@ const SHADOW_MODAL_STYLES = ` const prevLow = stats.previousLow; const realDropVsPrev = prevLow && prevLow > cardPrice ? Math.round(((prevLow - cardPrice) / prevLow) * 100) : (stats.realDiscountVsPrevLow || 0); - if (CONFIG.FILTERS_ENABLED !== false && isNonBest && CONFIG.REAL_DEAL_FILTER_ACTIVE) { + if (CONFIG.FILTER_BESTPREIS_ENABLED !== false && isNonBest && CONFIG.REAL_DEAL_FILTER_ACTIVE) { card.classList.add('tp-non-bestpreis-filtered'); } else { card.classList.remove('tp-non-bestpreis-filtered'); @@ -3082,9 +3130,12 @@ const SHADOW_MODAL_STYLES = ` showToast('Bestpreise-Modus deaktiviert'); }); emptyNotice.querySelector('#tp-empty-toggle-filters-btn')?.addEventListener('click', () => { - saveConfigKey('FILTERS_ENABLED', false); + saveConfigKey('FILTER_NEG_ENABLED', false); + saveConfigKey('FILTER_CAT_ENABLED', false); + saveConfigKey('FILTER_MIN_ENABLED', false); + saveConfigKey('FILTER_BESTPREIS_ENABLED', false); processListings(); - showToast('⏸️ Filter pausiert (alle Angebote sichtbar)'); + showToast('⏸️ Alle Filter pausiert (alle Angebote sichtbar)'); }); } else if (emptyNotice) { emptyNotice.remove(); @@ -3129,13 +3180,13 @@ const SHADOW_MODAL_STYLES = ` if (cd.pid && !cd.stats && cd.discountVal !== null && cd.discountVal >= minDealDiscount) { counts.uncheckedDeals++; } - if (CONFIG.FILTERS_ENABLED !== false && cd.isVerifiedNonBest && CONFIG.REAL_DEAL_FILTER_ACTIVE) { + if (CONFIG.FILTER_BESTPREIS_ENABLED !== false && cd.isVerifiedNonBest && CONFIG.REAL_DEAL_FILTER_ACTIVE) { counts.nonBest++; } cd.dealScore = computeDealScore(cd.stats, cd.cardPrice); if (cd.dealScore) { counts.bestpreiseDeals++; - } else if (CONFIG.FILTERS_ENABLED !== false && CONFIG.BESTPREISE_MODE_ACTIVE && cd.stats && !isStandardFiltered) { + } else if (CONFIG.FILTER_BESTPREIS_ENABLED !== false && CONFIG.BESTPREISE_MODE_ACTIVE && cd.stats && !isStandardFiltered) { counts.bestpreiseHidden++; } } @@ -3784,7 +3835,7 @@ const SHADOW_MODAL_STYLES = ` exportBtn?.addEventListener('click', () => { const exportData = { _meta: { - version: (typeof GM_info !== 'undefined' && GM_info?.script?.version) || '2.16.0', + version: (typeof GM_info !== 'undefined' && GM_info?.script?.version) || '2.18.18', exported: new Date().toISOString() }, config: { ...CONFIG }