# (c) cavaliba.com - test / field templates / edit mode golden markup

"""
Golden markup for display_mode "edit" (instance_edit / instance_new form rows),
one assertion per dataformat. Pins what each fields/_<dataformat>.html renders
today so the unification can prove it changed nothing - see
project/claude_doc/2026-10-03-FIELD-TEMPLATE-UNIFICATION-DONE.md.

Unlike the display modes, an edit snippet emits a whole Bootstrap row including
its own <label> - the caller supplies no wrapper. That asymmetry is the thing
most likely to break during the merge, so every assertion here includes the
label markup.

f_permission is asserted structurally rather than as a golden string: its
<select> carries one <option> per SirenePermission, so a literal expectation
would change every time a builtin permission is added.
"""

from django.test import TestCase

import app_home.cache as cache
from tests import helper

ROW = '<div class="mb-3 row">'
LABEL = '<label class="col-form-label col-sm-2">'
CHECK = '<div class="form-check col-sm-10">'
INPUT = '<input type="text" class="form-control col-sm-10"'


class TestFieldRenderEdit(TestCase):
    def setUp(self):
        """fieldrender schema + one fully populated instance."""
        cache.clear()
        self.instance = helper.add_fieldrender_fixture()

    def render(self, fieldname):
        """Normalized edit-mode markup for one field of the fixture instance."""
        return helper.render_field(self.instance, fieldname, "edit")

    def text_input_row(self, fieldname, label, value):
        """Expected markup for the plain text-input row shared by string/int/float/
        time. date/datetime moved to native pickers, so they assert their own
        markup below."""
        return (
            f"{ROW}{LABEL}{label}</label>{CHECK}{INPUT} "
            f'id="{fieldname}" name="{fieldname}" value="{value}" ></div></div>'
        )

    def test_string(self):
        self.assertEqual(
            self.render("f_string"),
            self.text_input_row("f_string", "F String", "plain string value"),
        )

    def test_string_safe_value_is_escaped_in_the_input(self):
        self.assertEqual(
            self.render("f_string_safe"),
            self.text_input_row("f_string_safe", "F String Safe", "&lt;b&gt;bold&lt;/b&gt;"),
        )

    def test_string_url_has_no_special_widget(self):
        self.assertEqual(
            self.render("f_string_url"),
            self.text_input_row("f_string_url", "F String Url", "https://example.test/doc"),
        )

    def test_string_multi_is_unsupported(self):
        self.assertEqual(
            self.render("f_string_multi"),
            f"{ROW}{LABEL}F String Multi</label>{CHECK}"
            ' Unsupported Multi-value Editor <div class="form-text"></div></div></div>',
        )

    def test_int_reads_the_joined_value_not_a_list(self):
        # int/float edit datapoints carry a space-joined string, so the template
        # uses item.value - not item.value.0 like string/date/text
        self.assertEqual(self.render("f_int"), self.text_input_row("f_int", "F Int", "42"))

    def test_float_is_unlocalized(self):
        self.assertEqual(self.render("f_float"), self.text_input_row("f_float", "F Float", "3.5"))

    def test_boolean_is_a_checked_checkbox(self):
        self.assertEqual(
            self.render("f_boolean"),
            f"{ROW}{LABEL}F Boolean</label>"
            '<div class="col-sm-10 form-check">'
            '<input class="form-check mt-2" type="checkbox" name="f_boolean" checked>'
            "</div></div>",
        )

    def test_date_is_a_native_date_picker(self):
        self.assertEqual(
            self.render("f_date"),
            f"{ROW}{LABEL}F Date</label>{CHECK}"
            '<input type="date" class="form-control col-sm-10" '
            'id="f_date" name="f_date" value="2026-01-15" ></div></div>',
        )

    def test_datetime_is_a_native_picker_with_a_T_separated_value(self):
        # 'YYYY-MM-DD hh:mm:ss' is stored, but <input type="datetime-local">
        # only accepts the 'T' separator on its value attribute
        self.assertEqual(
            self.render("f_datetime"),
            f"{ROW}{LABEL}F Datetime</label>{CHECK}"
            '<input type="datetime-local" step="1" class="form-control col-sm-10" '
            'id="f_datetime" name="f_datetime" value="2026-01-15T10:20:30" ></div></div>',
        )

    def test_time(self):
        self.assertEqual(self.render("f_time"), self.text_input_row("f_time", "F Time", "10:20:30"))

    def test_ipv4(self):
        # _ipv4.html closes the input without the trailing space the others have
        self.assertEqual(
            self.render("f_ipv4"),
            f"{ROW}{LABEL}F Ipv4</label>{CHECK}{INPUT} "
            'id="f_ipv4" name="f_ipv4" value="10.1.2.3"></div></div>',
        )

    def test_ipv4_subnet_has_no_special_widget(self):
        self.assertEqual(
            self.render("f_ipv4_subnet"),
            f"{ROW}{LABEL}F Ipv4 Subnet</label>{CHECK}{INPUT} "
            'id="f_ipv4_subnet" name="f_ipv4_subnet" value="10.1.2.0/24"></div></div>',
        )

    def test_text_is_a_plain_textarea(self):
        self.assertEqual(
            self.render("f_text"),
            f"{ROW}{LABEL}F Text</label>{CHECK}"
            '<pre><textarea class="form-control" id="f_text" name="f_text" rows="8">'
            f"{helper.LONG_TEXT}</textarea></pre></div></div>",
        )

    def test_text_html_is_a_tinymce_textarea(self):
        out = self.render("f_text_html")
        self.assertIn('<textarea class="tinymce"', out)
        self.assertIn('id="f_text_html"', out)
        self.assertIn("&quot;selector&quot;: &quot;#f_text_html&quot;", out)
        self.assertIn("&lt;p&gt;html body&lt;/p&gt;</textarea>", out)
        self.assertNotIn('<textarea class="form-control"', out)

    def test_text_yaml_is_a_plain_textarea(self):
        self.assertEqual(
            self.render("f_text_yaml"),
            f"{ROW}{LABEL}F Text Yaml</label>{CHECK}"
            '<pre><textarea class="form-control" id="f_text_yaml" name="f_text_yaml" rows="8">'
            "a: 1 b: 2 </textarea></pre></div></div>",
        )

    def test_schema_single_is_a_select_with_an_empty_option(self):
        self.assertEqual(
            self.render("f_schema"),
            f"{ROW}{LABEL}F Schema</label>{CHECK}"
            '<select class="form-check col-sm-10" name="f_schema" id="id_f_schema" '
            'data-cv-select2="simple">'
            '<option value="">---------------------------------</option>'
            '<option value="site01" selected> Site 01 </option>'
            '<option value="site02"> Site 02 </option>'
            "</select></div></div>",
        )

    def test_schema_multi_is_a_multiple_select_without_an_empty_option(self):
        self.assertEqual(
            self.render("f_schema_multi"),
            f"{ROW}{LABEL}F Schema Multi</label>{CHECK}"
            '<select class="form-check col-sm-10" name="f_schema_multi" '
            'id="id_f_schema_multi" multiple data-cv-select2="simple">'
            '<option value="site01" selected> Site 01 </option>'
            '<option value="site02" selected> Site 02 </option>'
            "</select></div></div>",
        )

    def test_schema_small_set_is_not_ajax(self):
        # two site instances is well under DATA_BIGSET_SIZE, so no ajax lookup
        out = self.render("f_schema")
        self.assertNotIn("ajax-instance", out)
        self.assertNotIn("data-cv-ajax-url", out)

    def test_enumerate_options_carry_widget_and_selection(self):
        self.assertEqual(
            self.render("f_enumerate"),
            f"{ROW}{LABEL}F Enumerate</label>{CHECK}"
            '<select class="form-check col-sm-10" name="f_enumerate" id="id_f_enumerate" '
            'data-cv-select2="simple">'
            '<option value="">----------------------</option>'
            '<option value="Minor">&#x1F7E1; &nbsp; Minor</option>'
            '<option value="Major" selected>&#x1f534; &nbsp; Major</option>'
            '<option value="Plain">Plain</option>'
            "</select></div></div>",
        )

    def test_permission_select_offers_every_permission_and_marks_the_stored_one(self):
        out = self.render("f_permission")
        self.assertIn(
            f"{ROW}{LABEL}F Permission</label>{CHECK}"
            '<select class="form-check col-sm-10" name="f_permission" id="id_f_permission" '
            'data-cv-select2="simple">'
            '<option value="">----------------------</option>',
            out,
        )
        self.assertIn('<option value="p_data_admin" selected>p_data_admin</option>', out)
        self.assertIn('<option value="p_schema_write">p_schema_write</option>', out)
        self.assertTrue(out.endswith("</select></div></div>"))

    def test_password_is_a_password_input_with_a_placeholder_value(self):
        self.assertEqual(
            self.render("f_password"),
            f"{ROW}{LABEL}F Password</label>{CHECK}"
            '<input type="password" class="form-control col-sm-10" '
            'id="f_password" name="f_password" value="******************************" >'
            "</div></div>",
        )

    def test_password_clear_value_never_reaches_the_form(self):
        self.assertNotIn("Secr3t", self.render("f_password"))

    def test_file_has_an_upload_input_a_clear_button_and_its_own_script(self):
        self.assertEqual(
            self.render("f_file"),
            f"{ROW}"
            '<label class="col-form-label col-sm-2"> F File </label>'
            f"{CHECK}"
            '<div class="input-group mb-2">'
            '<input type="file" class="form-control" name="file-f_file" id="id-file-f_file">'
            '<span class="btn btn-outline-secondary" id="id-clear-f_file">X</span></div>'
            '<div class="form-text mb-1"><i class="fa fa-upload ms-2 me-2"></i>upload</div>'
            '<div class="form-text mb-1"><i class="fa fa-file ms-2 me-2 mb-3"></i>'
            '<span id="id-current-f_file"> report.pdf (2048) </span>'
            '<input type="hidden" id="id-hidden-f_file" name="current-f_file" '
            f'value="{helper.FIELDRENDER_FILEID}"></div></div>'
            '<script> document.getElementById("id-clear-f_file")'
            '.addEventListener("click", clearInput_f_file ); '
            "function clearInput_f_file(){ "
            'document.getElementById("id-file-f_file").value = ""; '
            'document.getElementById("id-hidden-f_file").value = ""; '
            'document.getElementById("id-current-f_file").textContent = ""; } </script>'
            "</div>",
        )

    def test_external_renders_nothing(self):
        # external fields are filtered out of the form by get_dict_for_ui_form();
        # the template is defensive only
        self.assertEqual(self.render("_f_ext"), "")
