mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-05-29 21:11:50 +00:00
1a2e9309ed
- Implements configurable custom strings for restock detection (fixes #2779) - Adds robust text normalization (case-insensitive, accent removal, whitespace) - Supports international sites with custom messages like 'Pronto estarán en stock\!' - Makes built-in in-stock detection configurable (addresses TODO) - Includes comprehensive unit and integration tests 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
95 lines
4.1 KiB
Python
95 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import unittest
|
|
from changedetectionio.processors.restock_diff.processor import perform_site_check
|
|
|
|
|
|
class TestCustomStringNormalization(unittest.TestCase):
|
|
"""Test the text normalization logic for custom out-of-stock strings"""
|
|
|
|
def setUp(self):
|
|
# Create a processor instance for testing
|
|
self.processor = perform_site_check(datastore=None, watch_uuid='test')
|
|
|
|
def test_normalize_text_for_matching(self):
|
|
"""Test the _normalize_text_for_matching method"""
|
|
|
|
test_cases = [
|
|
# (input, expected_output)
|
|
("Agotado", "agotado"),
|
|
("AGOTADO", "agotado"), # Lowercase
|
|
("Sin stock!", "sin stock!"), # Normalize whitespace
|
|
("Pronto\t\nestarán\nen stock", "pronto estaran en stock"), # Multiple whitespace types + accents
|
|
("¡Temporalmente AGOTADO!", "¡temporalmente agotado!"), # Complex case
|
|
("", ""), # Empty string
|
|
("café", "cafe"), # French accent
|
|
("naïve", "naive"), # Multiple accents
|
|
]
|
|
|
|
for input_text, expected in test_cases:
|
|
with self.subTest(input_text=input_text):
|
|
result = self.processor._normalize_text_for_matching(input_text)
|
|
self.assertEqual(result, expected,
|
|
f"Failed to normalize '{input_text}' -> expected '{expected}', got '{result}'")
|
|
|
|
def test_check_custom_strings_normalization(self):
|
|
"""Test that custom string matching works with normalization"""
|
|
|
|
test_cases = [
|
|
# (page_text, custom_strings, should_match, description)
|
|
("AGOTADO", "agotado", True, "uppercase to lowercase"),
|
|
("Agotado", "agotado", True, "single uppercase to lowercase"),
|
|
("Sin stock!", "sin stock", True, "multiple spaces normalized"),
|
|
("¡Pronto estarán en stock!", "pronto estaran en stock", True, "accents + spaces"),
|
|
("TEMPORALMENTE AGOTADO", "temporalmente agotado", True, "multi-word uppercase"),
|
|
("Available now", "agotado", False, "no match case"),
|
|
("", "agotado", False, "empty text"),
|
|
("agotado", "", False, "empty custom strings"),
|
|
]
|
|
|
|
for page_text, custom_strings, should_match, description in test_cases:
|
|
with self.subTest(description=description):
|
|
result = self.processor._check_custom_strings(page_text, custom_strings, "out-of-stock")
|
|
|
|
if should_match:
|
|
self.assertIsNotNone(result,
|
|
f"Expected match for '{description}': '{page_text}' should match '{custom_strings}'")
|
|
else:
|
|
self.assertIsNone(result,
|
|
f"Expected no match for '{description}': '{page_text}' should not match '{custom_strings}'")
|
|
|
|
def test_check_custom_strings_multiline(self):
|
|
"""Test that multi-line custom strings work properly"""
|
|
|
|
page_text = "Product status: TEMPORALMENTE AGOTADO"
|
|
custom_strings = """
|
|
sin stock
|
|
agotado
|
|
temporalmente agotado
|
|
"""
|
|
|
|
result = self.processor._check_custom_strings(page_text, custom_strings, "out-of-stock")
|
|
self.assertIsNotNone(result)
|
|
self.assertEqual(result.strip(), "temporalmente agotado")
|
|
|
|
def test_get_combined_instock_strings_normalization(self):
|
|
"""Test that custom in-stock strings are normalized properly"""
|
|
|
|
restock_settings = {
|
|
'custom_instock_strings': 'Disponible AHORA\nEn Stock\nDISPONÍBLE'
|
|
}
|
|
|
|
result = self.processor._get_combined_instock_strings(restock_settings)
|
|
|
|
# Check that built-in strings are included
|
|
self.assertIn('instock', result)
|
|
self.assertIn('presale', result)
|
|
|
|
# Check that custom strings are normalized and included
|
|
self.assertIn('disponible ahora', result)
|
|
self.assertIn('en stock', result)
|
|
self.assertIn('disponible', result) # accent removed
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main() |