mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-07-08 08:21:14 +00:00
Adding ternary field
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from '_helpers.html' import render_field, render_checkbox_field, render_button, render_time_schedule_form %}
|
||||
{% from '_helpers.html' import render_field, render_checkbox_field, render_button, render_time_schedule_form, render_ternary_field %}
|
||||
{% from '_common_fields.html' import render_common_settings_form %}
|
||||
<script>
|
||||
const notification_base_url="{{url_for('ui.ui_notification.ajax_callback_send_notification_test', mode="global-settings")}}";
|
||||
@@ -84,11 +84,11 @@
|
||||
<span class="pure-form-message-inline">Love RSS? Does your reader support HTML? Set it here</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.application.form.use_page_title_in_list) }}
|
||||
{{ render_ternary_field(form.application.form.use_page_title_in_list, BooleanField=true) }}
|
||||
<span class="pure-form-message-inline">Note: This will automatically apply to all existing watches.</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.application.form.empty_pages_are_a_change) }}
|
||||
{{ render_ternary_field(form.application.form.empty_pages_are_a_change, BooleanField=true) }}
|
||||
<span class="pure-form-message-inline">When a request returns no content, or the HTML does not contain any text, is this considered a change?</span>
|
||||
</div>
|
||||
{% if form.requests.proxy %}
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
<div class="tab-pane-inner" id="notifications">
|
||||
<fieldset>
|
||||
<div class="pure-control-group inline-radio">
|
||||
{{ render_checkbox_field(form.notification_muted) }}
|
||||
{{ render_ternary_field(form.notification_muted) }}
|
||||
</div>
|
||||
{% if 1 %}
|
||||
<div class="pure-control-group inline-radio">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
{% from '_helpers.html' import render_field, render_checkbox_field, render_button, render_time_schedule_form, playwright_warning, only_playwright_type_watches_warning, render_conditions_fieldlist_of_formfields_as_table %}
|
||||
{% from '_helpers.html' import render_field, render_checkbox_field, render_button, render_time_schedule_form, playwright_warning, only_playwright_type_watches_warning, render_conditions_fieldlist_of_formfields_as_table, render_ternary_field %}
|
||||
{% from '_common_fields.html' import render_common_settings_form %}
|
||||
<script src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
|
||||
<script src="{{url_for('static_content', group='js', filename='vis.js')}}" defer></script>
|
||||
@@ -103,7 +103,7 @@
|
||||
<br>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.use_page_title_in_list) }}
|
||||
{{ render_ternary_field(form.use_page_title_in_list) }}
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.filter_failure_notification_send) }}
|
||||
@@ -199,7 +199,7 @@ Math: {{ 1 + 1 }}") }}
|
||||
</div>
|
||||
<fieldset data-visible-for="fetch_backend=html_requests fetch_backend=html_webdriver" >
|
||||
<div class="pure-control-group inline-radio advanced-options" style="display: none;">
|
||||
{{ render_checkbox_field(form.ignore_status_codes) }}
|
||||
{{ render_ternary_field(form.ignore_status_codes, BooleanField=true) }}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
@@ -263,11 +263,11 @@ Math: {{ 1 + 1 }}") }}
|
||||
<div class="tab-pane-inner" id="notifications">
|
||||
<fieldset>
|
||||
<div class="pure-control-group inline-radio">
|
||||
{{ render_checkbox_field(form.notification_muted) }}
|
||||
{{ render_ternary_field(form.notification_muted) }}
|
||||
</div>
|
||||
{% if watch_needs_selenium_or_playwright %}
|
||||
<div class="pure-control-group inline-radio">
|
||||
{{ render_checkbox_field(form.notification_screenshot) }}
|
||||
{{ render_ternary_field(form.notification_screenshot, BooleanField=true) }}
|
||||
<span class="pure-form-message-inline">
|
||||
<strong>Use with caution!</strong> This will easily fill up your email storage quota or flood other storages.
|
||||
</span>
|
||||
@@ -323,27 +323,27 @@ Math: {{ 1 + 1 }}") }}
|
||||
<fieldset class="pure-group" id="text-filtering-type-options">
|
||||
<h3>Text filtering</h3>
|
||||
Limit trigger/ignore/block/extract to;<br>
|
||||
{{ render_checkbox_field(form.filter_text_added) }}
|
||||
{{ render_checkbox_field(form.filter_text_replaced) }}
|
||||
{{ render_checkbox_field(form.filter_text_removed) }}
|
||||
{{ render_ternary_field(form.filter_text_added, BooleanField=true) }}
|
||||
{{ render_ternary_field(form.filter_text_replaced, BooleanField=true) }}
|
||||
{{ render_ternary_field(form.filter_text_removed, BooleanField=true) }}
|
||||
<span class="pure-form-message-inline">Note: Depending on the length and similarity of the text on each line, the algorithm may consider an <strong>addition</strong> instead of <strong>replacement</strong> for example.</span><br>
|
||||
<span class="pure-form-message-inline"> So it's always better to select <strong>Added</strong>+<strong>Replaced</strong> when you're interested in new content.</span><br>
|
||||
<span class="pure-form-message-inline"> When content is merely moved in a list, it will also trigger an <strong>addition</strong>, consider enabling <code><strong>Only trigger when unique lines appear</strong></code></span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-control-group">
|
||||
{{ render_checkbox_field(form.check_unique_lines) }}
|
||||
{{ render_ternary_field(form.check_unique_lines, BooleanField=true) }}
|
||||
<span class="pure-form-message-inline">Good for websites that just move the content around, and you want to know when NEW content is added, compares new lines against all history for this watch.</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-control-group">
|
||||
{{ render_checkbox_field(form.remove_duplicate_lines) }}
|
||||
{{ render_ternary_field(form.remove_duplicate_lines, BooleanField=true) }}
|
||||
<span class="pure-form-message-inline">Remove duplicate lines of text</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-control-group">
|
||||
{{ render_checkbox_field(form.sort_text_alphabetically) }}
|
||||
{{ render_ternary_field(form.sort_text_alphabetically, BooleanField=true) }}
|
||||
<span class="pure-form-message-inline">Helps reduce changes detected caused by sites shuffling lines around, combine with <i>check unique lines</i> below.</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-control-group">
|
||||
{{ render_checkbox_field(form.trim_text_whitespace) }}
|
||||
{{ render_ternary_field(form.trim_text_whitespace, BooleanField=true) }}
|
||||
<span class="pure-form-message-inline">Remove any whitespace before and after each line of text</span>
|
||||
</fieldset>
|
||||
{% include "edit/text-options.html" %}
|
||||
|
||||
@@ -28,6 +28,8 @@ from wtforms.validators import ValidationError
|
||||
|
||||
from validators.url import url as url_validator
|
||||
|
||||
from changedetectionio.widgets import TernaryNoneBooleanField
|
||||
|
||||
|
||||
# default
|
||||
# each select <option data-enabled="enabled-0-0"
|
||||
@@ -548,7 +550,7 @@ class commonSettingsForm(Form):
|
||||
self.notification_title.extra_notification_tokens = kwargs.get('extra_notification_tokens', {})
|
||||
self.notification_urls.extra_notification_tokens = kwargs.get('extra_notification_tokens', {})
|
||||
|
||||
use_page_title_in_list = BooleanField('Use page <title> in watch overview list', default=True)
|
||||
use_page_title_in_list = TernaryNoneBooleanField('Use page <title> in watch overview list', default=None) #BooleanField=True
|
||||
fetch_backend = RadioField(u'Fetch Method', choices=content_fetchers.available_fetchers(), validators=[ValidateContentFetcherIsReady()])
|
||||
notification_body = TextAreaField('Notification Body', default='{{ watch_url }} had a change.', validators=[validators.Optional(), ValidateJinja2Template()])
|
||||
notification_format = SelectField('Notification format', choices=valid_notification_formats.keys())
|
||||
@@ -621,8 +623,7 @@ class processor_text_json_diff_form(commonSettingsForm):
|
||||
proxy = RadioField('Proxy')
|
||||
filter_failure_notification_send = BooleanField(
|
||||
'Send a notification when the filter can no longer be found on the page', default=False)
|
||||
|
||||
notification_muted = BooleanField('Notifications Muted / Off', default=False)
|
||||
notification_muted = TernaryNoneBooleanField('Notifications', default=None, yes_text="Muted", no_text="On", none_text="System default")
|
||||
notification_screenshot = BooleanField('Attach screenshot to notification (where possible)', default=False)
|
||||
|
||||
conditions_match_logic = RadioField(u'Match', choices=[('ALL', 'Match all of the following'),('ANY', 'Match any of the following')], default='ALL')
|
||||
@@ -764,7 +765,7 @@ class globalSettingsApplicationForm(commonSettingsForm):
|
||||
validators=[validators.Optional()],
|
||||
render_kw={"placeholder": os.getenv('BASE_URL', 'Not set')}
|
||||
)
|
||||
empty_pages_are_a_change = BooleanField('Treat empty pages as a change?', default=False)
|
||||
empty_pages_are_a_change = TernaryNoneBooleanField('Treat empty pages as a change?', default=False)
|
||||
fetch_backend = RadioField('Fetch Method', default="html_requests", choices=content_fetchers.available_fetchers(), validators=[ValidateContentFetcherIsReady()])
|
||||
global_ignore_text = StringListField('Ignore Text', [ValidateListRegex()])
|
||||
global_subtractive_selectors = StringListField('Remove elements', [ValidateCSSJSONXPATHInput(allow_json=False)])
|
||||
|
||||
@@ -24,6 +24,23 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_ternary_field(field, BooleanField=false) %}
|
||||
{% if BooleanField %}
|
||||
{% set _ = field.__setattr__('boolean_mode', true) %}
|
||||
{% endif %}
|
||||
<div class="ternary-field {% if field.errors %} error {% endif %}">
|
||||
<div class="ternary-field-label">{{ field.label }}</div>
|
||||
<div class="ternary-field-widget">{{ field(**kwargs)|safe }}</div>
|
||||
{% if field.errors %}
|
||||
<ul class=errors>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro render_simple_field(field) %}
|
||||
<span class="label {% if field.errors %}error{% endif %}">{{ field.label }}</span>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from .ternary_boolean import TernaryNoneBooleanWidget, TernaryNoneBooleanField
|
||||
|
||||
__all__ = ['TernaryNoneBooleanWidget', 'TernaryNoneBooleanField']
|
||||
@@ -0,0 +1,187 @@
|
||||
from wtforms import Field
|
||||
from wtforms import widgets
|
||||
from markupsafe import Markup
|
||||
|
||||
class TernaryNoneBooleanWidget:
|
||||
"""
|
||||
A widget that renders a horizontal radio button group with either two options (Yes/No)
|
||||
or three options (Yes/No/Default), depending on the field's boolean_mode setting.
|
||||
"""
|
||||
def __call__(self, field, **kwargs):
|
||||
html = ['<div class="ternary-radio-group pure-form">']
|
||||
|
||||
field_id = kwargs.pop('id', field.id)
|
||||
boolean_mode = getattr(field, 'boolean_mode', False)
|
||||
|
||||
# Get custom text or use defaults
|
||||
yes_text = getattr(field, 'yes_text', 'Yes')
|
||||
no_text = getattr(field, 'no_text', 'No')
|
||||
none_text = getattr(field, 'none_text', 'Default')
|
||||
|
||||
# True option
|
||||
checked_true = ' checked' if field.data is True else ''
|
||||
html.append(f'''
|
||||
<label class="ternary-radio-option">
|
||||
<input type="radio" name="{field.name}" value="true" id="{field_id}_true"{checked_true} class="pure-radio">
|
||||
<span class="ternary-radio-label pure-button-primary">{yes_text}</span>
|
||||
</label>
|
||||
''')
|
||||
|
||||
# False option
|
||||
checked_false = ' checked' if field.data is False else ''
|
||||
html.append(f'''
|
||||
<label class="ternary-radio-option">
|
||||
<input type="radio" name="{field.name}" value="false" id="{field_id}_false"{checked_false} class="pure-radio">
|
||||
<span class="ternary-radio-label">{no_text}</span>
|
||||
</label>
|
||||
''')
|
||||
|
||||
# None option (only show if not in boolean mode)
|
||||
if not boolean_mode:
|
||||
checked_none = ' checked' if field.data is None else ''
|
||||
html.append(f'''
|
||||
<label class="ternary-radio-option">
|
||||
<input type="radio" name="{field.name}" value="none" id="{field_id}_none"{checked_none} class="pure-radio">
|
||||
<span class="ternary-radio-label ternary-default">{none_text}</span>
|
||||
</label>
|
||||
''')
|
||||
|
||||
html.append('</div>')
|
||||
|
||||
# Add CSS styles
|
||||
html.append('''
|
||||
<style>
|
||||
.ternary-radio-group {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
width: fit-content;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.ternary-radio-option {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ternary-radio-option input[type="radio"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.ternary-radio-label {
|
||||
padding: 8px 16px;
|
||||
background: #f7f7f7;
|
||||
border: none;
|
||||
border-right: 1px solid #ddd;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ternary-radio-option:last-child .ternary-radio-label {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.ternary-radio-option input:checked + .ternary-radio-label {
|
||||
background: #1f8dd6;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ternary-radio-option input:checked + .ternary-radio-label.ternary-default {
|
||||
background: #999;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.ternary-radio-option:hover .ternary-radio-label {
|
||||
background: #e6e6e6;
|
||||
}
|
||||
|
||||
.ternary-radio-option input:checked + .ternary-radio-label:hover {
|
||||
background: #1a7bc4;
|
||||
}
|
||||
|
||||
.ternary-radio-option input:checked + .ternary-radio-label.ternary-default:hover {
|
||||
background: #777;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.ternary-radio-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ternary-radio-label {
|
||||
flex: 1;
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
''')
|
||||
|
||||
return Markup(''.join(html))
|
||||
|
||||
class TernaryNoneBooleanField(Field):
|
||||
"""
|
||||
A field that can handle True, False, or None values, represented as a horizontal radio group.
|
||||
When boolean_mode=True, it acts like a BooleanField (only Yes/No options).
|
||||
When boolean_mode=False (default), it shows Yes/No/Default options.
|
||||
|
||||
Custom text can be provided for each option:
|
||||
- yes_text: Text for True option (default: "Yes")
|
||||
- no_text: Text for False option (default: "No")
|
||||
- none_text: Text for None option (default: "Default")
|
||||
"""
|
||||
widget = TernaryNoneBooleanWidget()
|
||||
|
||||
def __init__(self, label=None, validators=None, false_values=None, boolean_mode=False,
|
||||
yes_text="Yes", no_text="No", none_text="Default", **kwargs):
|
||||
super(TernaryNoneBooleanField, self).__init__(label, validators, **kwargs)
|
||||
|
||||
self.boolean_mode = boolean_mode
|
||||
self.yes_text = yes_text
|
||||
self.no_text = no_text
|
||||
self.none_text = none_text
|
||||
|
||||
if false_values is None:
|
||||
self.false_values = {'false', ''}
|
||||
else:
|
||||
self.false_values = false_values
|
||||
|
||||
def process_formdata(self, valuelist):
|
||||
if not valuelist or not valuelist[0]:
|
||||
# In boolean mode, default to False instead of None
|
||||
self.data = False if self.boolean_mode else None
|
||||
elif valuelist[0].lower() == 'true':
|
||||
self.data = True
|
||||
elif valuelist[0].lower() == 'false':
|
||||
self.data = False
|
||||
elif valuelist[0].lower() == 'none':
|
||||
# In boolean mode, treat 'none' as False
|
||||
self.data = False if self.boolean_mode else None
|
||||
else:
|
||||
self.data = False if self.boolean_mode else None
|
||||
|
||||
def _value(self):
|
||||
if self.data is True:
|
||||
return 'true'
|
||||
elif self.data is False:
|
||||
return 'false'
|
||||
else:
|
||||
# In boolean mode, None should be treated as False
|
||||
if self.boolean_mode:
|
||||
return 'false'
|
||||
else:
|
||||
return 'none'
|
||||
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
|
||||
|
||||
from changedetectionio.widgets import TernaryNoneBooleanField
|
||||
from wtforms import Form
|
||||
|
||||
class TestForm(Form):
|
||||
# Default text
|
||||
default_field = TernaryNoneBooleanField('Default Field', default=None)
|
||||
|
||||
# Custom text with HTML icons
|
||||
notification_field = TernaryNoneBooleanField(
|
||||
'Notifications',
|
||||
default=False,
|
||||
yes_text='🔕 Muted',
|
||||
no_text='🔔 Unmuted',
|
||||
none_text='⚙️ System default'
|
||||
)
|
||||
|
||||
# HTML with styling
|
||||
styled_field = TernaryNoneBooleanField(
|
||||
'Status',
|
||||
default=None,
|
||||
yes_text='<strong style="color: green;">✅ Active</strong>',
|
||||
no_text='<strong style="color: red;">❌ Inactive</strong>',
|
||||
none_text='<em style="color: gray;">🔧 Auto</em>'
|
||||
)
|
||||
|
||||
# Boolean mode with custom text
|
||||
boolean_field = TernaryNoneBooleanField(
|
||||
'Boolean Field',
|
||||
default=True,
|
||||
boolean_mode=True,
|
||||
yes_text="Enabled",
|
||||
no_text="Disabled"
|
||||
)
|
||||
|
||||
# FontAwesome example
|
||||
fontawesome_field = TernaryNoneBooleanField(
|
||||
'Notifications with FontAwesome',
|
||||
default=None,
|
||||
yes_text='<i class="fa fa-bell-slash"></i> Muted',
|
||||
no_text='<i class="fa fa-bell"></i> Unmuted',
|
||||
none_text='<i class="fa fa-cogs"></i> System default'
|
||||
)
|
||||
|
||||
def test_custom_text():
|
||||
"""Test custom text functionality"""
|
||||
|
||||
form = TestForm()
|
||||
|
||||
print("=== Testing TernaryNoneBooleanField Custom Text ===")
|
||||
|
||||
# Test default field
|
||||
print("\n--- Default Field ---")
|
||||
default_field = form.default_field
|
||||
default_html = default_field.widget(default_field)
|
||||
print(f"Contains 'Yes': {'Yes' in default_html}")
|
||||
print(f"Contains 'No': {'No' in default_html}")
|
||||
print(f"Contains 'Default': {'Default' in default_html}")
|
||||
assert 'Yes' in default_html and 'No' in default_html and 'Default' in default_html
|
||||
|
||||
# Test custom text field
|
||||
print("\n--- Custom Text Field with Emojis ---")
|
||||
notification_field = form.notification_field
|
||||
notification_html = notification_field.widget(notification_field)
|
||||
print(f"Contains '🔕 Muted': {'🔕 Muted' in notification_html}")
|
||||
print(f"Contains '🔔 Unmuted': {'🔔 Unmuted' in notification_html}")
|
||||
print(f"Contains '⚙️ System default': {'⚙️ System default' in notification_html}")
|
||||
print(f"Does NOT contain 'Yes': {'Yes' not in notification_html}")
|
||||
print(f"Does NOT contain 'No': {'No' not in notification_html}")
|
||||
assert '🔕 Muted' in notification_html and '🔔 Unmuted' in notification_html
|
||||
assert 'Yes' not in notification_html and 'No' not in notification_html
|
||||
|
||||
# Test HTML styling
|
||||
print("\n--- HTML Styled Field ---")
|
||||
styled_field = form.styled_field
|
||||
styled_html = styled_field.widget(styled_field)
|
||||
print(f"Contains HTML tags: {'<strong' in styled_html}")
|
||||
print(f"Contains color styling: {'color: green' in styled_html}")
|
||||
print(f"Contains emojis: {'✅' in styled_html and '❌' in styled_html}")
|
||||
assert '<strong' in styled_html and 'color: green' in styled_html
|
||||
|
||||
# Test boolean mode with custom text
|
||||
print("\n--- Boolean Field with Custom Text ---")
|
||||
boolean_field = form.boolean_field
|
||||
boolean_html = boolean_field.widget(boolean_field)
|
||||
print(f"Contains 'Enabled': {'Enabled' in boolean_html}")
|
||||
print(f"Contains 'Disabled': {'Disabled' in boolean_html}")
|
||||
print(f"Does NOT contain 'System default': {'System default' not in boolean_html}")
|
||||
print(f"Does NOT contain 'Default': {'Default' not in boolean_html}")
|
||||
assert 'Enabled' in boolean_html and 'Disabled' in boolean_html
|
||||
assert 'System default' not in boolean_html and 'Default' not in boolean_html
|
||||
|
||||
# Test FontAwesome field
|
||||
print("\n--- FontAwesome Icons Field ---")
|
||||
fontawesome_field = form.fontawesome_field
|
||||
fontawesome_html = fontawesome_field.widget(fontawesome_field)
|
||||
print(f"Contains FontAwesome classes: {'fa fa-bell' in fontawesome_html}")
|
||||
print(f"Contains multiple FA icons: {'fa fa-bell-slash' in fontawesome_html and 'fa fa-cogs' in fontawesome_html}")
|
||||
assert 'fa fa-bell' in fontawesome_html
|
||||
|
||||
print("\n✅ All custom text tests passed!")
|
||||
print("\n--- Example Usage ---")
|
||||
print("TernaryNoneBooleanField('Status', yes_text='🟢 Online', no_text='🔴 Offline', none_text='🟡 Auto')")
|
||||
print("TernaryNoneBooleanField('Notifications', yes_text='<i class=\"fa fa-bell-slash\"></i> Muted', ...)")
|
||||
|
||||
def test_data_processing():
|
||||
"""Test that custom text doesn't affect data processing"""
|
||||
print("\n=== Testing Data Processing ===")
|
||||
|
||||
form = TestForm()
|
||||
field = form.notification_field
|
||||
|
||||
# Test form data processing
|
||||
field.process_formdata(['true'])
|
||||
assert field.data is True, "Custom text should not affect data processing"
|
||||
print("✅ True processing works with custom text")
|
||||
|
||||
field.process_formdata(['false'])
|
||||
assert field.data is False, "Custom text should not affect data processing"
|
||||
print("✅ False processing works with custom text")
|
||||
|
||||
field.process_formdata(['none'])
|
||||
assert field.data is None, "Custom text should not affect data processing"
|
||||
print("✅ None processing works with custom text")
|
||||
|
||||
print("✅ All data processing tests passed!")
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_custom_text()
|
||||
test_data_processing()
|
||||
Reference in New Issue
Block a user