# -*- coding: utf-8 -*- """The full view of the configuration: label-first, and what opens itself. `vibe.configpage` is the SECONDARY surface of the Project Config button. The button itself is a four-module window of controls (`vibe.configform`); this is the "Full view" behind it -- every setting, every layer, and the provenance of every value -- printed to the output panel and written whole to a file beside the model. It has no Revit in it: the button passes in the layers it read, and everything below is that contract exercised on host CPython. Two of these tests are regressions for defects the owner hit on one press on 2026-08-30, and they are assertions rather than comments because both read as cosmetic and neither was: * `test_no_row_is_named_by_its_dotted_key` -- the screen offered `handling.report_dir` as the name of an option when the row had carried the label "Report folder" all along. * `test_*_opens_itself` -- the fold contract: if nothing below the headline is open, nothing below needs the reader. A section that opens has to say why in a sentence. And one is the seam with the window: `test_every_row_says_where_it_is_changed` keeps the two surfaces from becoming two answers to the same question. """ from vibe import configmodel from vibe import configpage from vibe import foldreport as fold # --------------------------------------------------------------- the fixture def _tree(pairs): out = {} for key, value in pairs: node = out parts = key.split('.') for part in parts[:-1]: node = node.setdefault(part, {}) node[parts[-1]] = value return out def _sample(row): """A type-appropriate value for one SETTINGS row.""" kind = row['kind'] if kind == 'choice': return (row['choices'] or ('fast',))[0] if kind == 'bool': return True if kind in ('float', 'int'): return 2 if kind == 'int' else 2.5 if kind == 'list': return ['ANCHOR_FACE_4T'] if kind == 'map': return {'3.3': 'no piece on this job has one'} if kind == 'stages': return [{'name': 'stripping', 'pci_case': 'stripping', 'strength': 'fci'}] return 'something' def _full(): return _tree([(row['key'], _sample(row)) for row in configmodel.SETTINGS]) def layers(defaults=None, plant=None, job=None, config=None, plant_exists=True): """[(label, path, exists, data)] the way `configdump.layer_values` gives it, with the project config layer added only when there is one.""" out = [('defaults', None, True, defaults if defaults is not None else _full()), ('catalogue', 'C:/tool/catalogue.yaml', True, {}), ('plant', 'C:/plant/knowledge.yaml', plant_exists, plant or {}), ('job', 'C:/job/knowledge.yaml', True, job or {})] if config is not None: out.append(('config', 'C:/job/sample_job-config.yaml', True, config)) return out def rendered(**over): kwargs = {'title': 'sample_job', 'layers': layers(), 'job_path': 'C:/job/knowledge.yaml', 'env': [('WILLTOOLS_KNOWLEDGE', '(not set)')], 'run_options': {}, 'run_options_path': 'C:/job/run-options.yaml', 'locations': {'report_dir': 'C:/job/reports'}, 'report_dir': 'C:/job/reports', 'subtrees': [('parameters', 'the office template', [('member.control_number', 'Control Number', 'plant')])], 'muted': {}} kwargs.update(over) return configpage.render(**kwargs) def by_key(sections): return dict((section.key, section) for section in sections) # ----------------------------------------------------------------- the rows def test_rows_keep_a_stable_position(): rows = configpage.editable_rows(layers()) assert [row['n'] for row in rows] == list(range(1, len(rows) + 1)) def test_every_editable_setting_is_on_the_page_exactly_once(): rows = configpage.editable_rows(layers()) keys = [row['key'] for row in rows] assert keys == [row['key'] for row in configmodel.editable()] assert len(set(keys)) == len(keys) def test_no_row_is_named_by_its_dotted_key(): """The defect: the button offered `handling.report_dir` as an option name. Every SETTINGS row carries a human label and this is what makes sure the page uses it. The key is still on the row -- it is what somebody grepping the YAML needs -- but it is never the name. """ for row in configpage.editable_rows(layers()): assert row['label'], row['key'] assert row['label'] != row['key'] assert '.' not in row['label'] or ' ' in row['label'] def test_rows_group_under_the_human_section_names(): rows = configpage.editable_rows(layers()) groups = configpage.group_rows(rows) names = [name for name, _rows in groups] assert names == [name for name in configmodel.sections() if any(row['section'] == name for row in rows)] assert 'Output' in names # A section name is prose, not a path fragment. for name in names: assert '.' not in name def test_grouping_preserves_the_settings_order(): rows = configpage.editable_rows(layers()) seen = [] for _name, group in configpage.group_rows(rows): seen.extend([row['n'] for row in group]) assert seen == sorted(seen) # ------------------------------------------------------------- value_text() def test_an_unknown_value_is_a_sentence_and_never_a_blank(): res = configmodel.resolve(layers(defaults={}), 'handling.rig_mode') assert not res['known'] assert configpage.value_text('handling.rig_mode', res) == \ configpage.NOT_SET assert configpage.value_text('handling.rig_mode', res).strip() def test_fast_precise_is_shown_the_way_the_config_file_writes_it(): tree = _full() tree['handling']['fast_lifter_box'] = False res = configmodel.resolve(layers(defaults=tree), 'handling.fast_lifter_box') assert configpage.value_text('handling.fast_lifter_box', res) == 'precise' # ------------------------------------------------------------- the headline def test_the_headline_is_first_and_is_never_folded(): sections = rendered() assert sections[0].headline is True assert sections[0].is_open is True assert all(not section.headline for section in sections[1:]) def test_the_headline_is_label_first_with_the_key_as_small_print(): sections = rendered() tables = [block for block in sections[0].blocks if block[0] == fold.TABLE] assert tables columns = tables[0][1] assert columns[0] == 'setting' assert columns[-1] == 'key' first = tables[0][2][0] assert first[0] != first[-1] # the label is not the key assert first[-1].startswith('`') # the key is reference small print def test_every_row_says_where_it_is_changed(): """The column that makes this page the other half of the four-module window instead of a second, competing surface.""" sections = rendered(placement={'handling.rig_mode': 'Lifting and handling'}) table = [b for b in sections[0].blocks if b[0] == fold.TABLE][0] assert table[1][-2] == 'change it in' wheres = set(row[-2] for row in table[2]) assert 'Lifting and handling' in wheres or 'the file' in wheres for row in table[2]: assert row[-2], 'a blank here reads as "nowhere"' def test_a_setting_no_box_carries_says_it_is_changed_in_the_file(): sections = rendered(placement={}) table = [b for b in sections[0].blocks if b[0] == fold.TABLE][0] assert set(row[-2] for row in table[2]) == set(['the file']) def test_without_a_placement_the_column_is_simply_empty(): """The page still renders for a caller that has no window to point at.""" sections = rendered() table = [b for b in sections[0].blocks if b[0] == fold.TABLE][0] assert set(row[-2] for row in table[2]) == set(['']) def test_the_headline_says_where_the_edit_goes(): text = '\n'.join(block[1] for block in rendered()[0].blocks if block[0] == fold.MD) assert 'C:/job/knowledge.yaml' in text assert 'job layer' in text def test_a_build_note_reaches_the_top_of_the_page(): text = '\n'.join(block[1] for block in rendered(notes=['restart for the full view'])[0].blocks if block[0] == fold.MD) assert 'restart for the full view' in text # -------------------------------------------------- what opens itself, and why def test_nothing_opens_when_nothing_is_wrong(): for section in rendered()[1:]: assert section.problem is None, section.key assert section.is_open is False def test_a_setting_no_layer_sets_opens_its_own_section(): tree = _full() del tree['handling']['sling_angle_deg'] sections = by_key(rendered(layers=layers(defaults=tree))) assert 'unknown' in sections assert sections['unknown'].is_open assert 'nothing in any layer decides' in sections['unknown'].problem def test_a_missing_layer_file_opens_the_layers_section(): sections = by_key(rendered(layers=layers(plant_exists=False))) assert sections['layers'].is_open assert 'plant' in sections['layers'].problem def test_a_live_config_file_opens_its_own_section_and_names_what_it_decides(): given = layers(config={'handling': {'rig_mode': 'rolling'}}) sections = by_key(rendered(layers=given, rows=None, config_path='C:/job/sample_job-config.yaml')) assert sections['config'].is_open assert 'NOT' in sections['config'].problem body = '\n'.join(block[1] for block in sections['config'].blocks if block[0] == fold.MD) assert 'sample_job-config.yaml' in body def test_a_missing_config_file_says_so_rather_than_falling_back_quietly(): sections = by_key(rendered(config_path='C:/gone/config.yaml', config_missing=True)) assert 'MISSING' in sections['config'].problem def test_the_config_chip_marks_the_rows_the_file_decides(): given = layers(config={'handling': {'rig_mode': 'rolling'}}) rows = configpage.editable_rows(given) marked = configpage.config_rows(rows) assert [row['key'] for row in marked] == ['handling.rig_mode'] assert marked[0]['chip'] == '!' def test_muted_items_open_their_section_and_are_listed_with_the_reason(): sections = by_key(rendered(muted={'3.3': 'no piece on this job has one'})) assert sections['muted'].is_open assert '1 checklist item' in sections['muted'].problem rows = [block for block in sections['muted'].blocks if block[0] == fold.TABLE][0][2] assert rows[0][0] == '3.3' assert 'no piece' in rows[0][1] def test_an_unmapped_parameter_tree_opens_and_says_the_checks_go_quiet(): sections = by_key(rendered(subtrees=[('parameters', 'the template', [])])) assert sections['params'].is_open assert 'do not run' in sections['params'].problem # ------------------------------------------------------------- the read-only def test_every_read_only_setting_carries_the_sentence_saying_why(): section = by_key(rendered())['readonly'] body = '\n'.join(block[1] for block in section.blocks if block[0] == fold.MD) for row in configmodel.read_only(): assert row['label'] in body assert (row['why'] or '')[:40] in body def test_the_read_only_table_is_label_first_with_the_key_alongside(): section = by_key(rendered())['readonly'] table = [block for block in section.blocks if block[0] == fold.TABLE][0] assert table[1][0] == 'setting' assert table[1][-1] == 'key' for row in table[2]: assert row[0] != row[-1] # ------------------------------------------------------- one setting up close # --------------------------------------------------------- the render itself def test_the_whole_page_renders_as_text_with_nothing_folded(): body = fold.text(rendered(), show_all=True) assert 'Configuration of sample_job' in body for section in rendered()[1:]: assert section.title.replace('—', '--') in body def test_the_folded_half_renders_as_one_line_of_html(): """`print_html` is a `print(..., end='')` into the panel's stream and every newline in it starts a new output entry, which would cut a div in half.""" body = fold.html(rendered()[1:]) assert body assert '\n' not in body def test_a_pipe_in_a_typed_reason_cannot_split_a_table_row(): sections = by_key(rendered(muted={'3.3': 'a | b'})) rows = [block for block in sections['muted'].blocks if block[0] == fold.TABLE][0][2] assert '|' not in rows[0][1] assert '|' in rows[0][1] def test_the_page_names_the_file_it_was_also_written_to(): body = fold.text(rendered(full_path='C:/job/config/last-page.txt')) assert 'last-page.txt' in body def test_every_section_has_a_summary_so_folded_is_not_gone(): for section in rendered()[1:]: assert section.summary or section.problem, section.key # ------------------------------------------- the muted section, and its seam def _mute_rows(*rows): """`configform.mute_table` output, spelled out rather than imported. The page takes these as DATA and never builds them: they need the ticket checklist table, which `configpage` has no business loading. Writing the shape here is what keeps that contract visible. """ out = [] for number, name, reason, layer, editable in rows: out.append({'number': number, 'name': name, 'grade': 'A', 'known': not name.startswith('('), 'muted': True, 'reason': reason, 'layer': layer, 'editable': editable, 'why_not': None if editable else 'set by ' + layer, 'falls_back': None}) return out def test_the_muted_section_says_where_the_control_is(): """The defect this is for: he went looking for the mute control twice and did not find it, and this page -- the one surface that lists what is muted -- said nothing at all about where muting is done.""" section = by_key(rendered(muted={'3.3': 'no piece has one'}))['muted'] body = '\n'.join(block[1] for block in section.blocks if block[0] == fold.MD) assert 'Ticket QC' in body assert 'speaker' in body def test_the_muted_section_says_the_check_still_runs(): """Muting is a volume control, not a switch. A page that showed a mute without saying that would leave the reader believing a check was off.""" section = by_key(rendered(muted={'3.3': 'no piece has one'}))['muted'] body = '\n'.join(block[1] for block in section.blocks if block[0] == fold.MD) assert 'does not stop a check' in body assert 'never quietened' in body.lower() assert 'still RUNS' in section.problem or 'still' in section.problem def test_a_mute_this_window_cannot_remove_is_named_with_its_layer(): rows = _mute_rows(('3.3', 'release ratio', 'the plant never has one', 'plant', False)) section = by_key(rendered(muted={'3.3': 'the plant never has one'}, muted_rows=rows))['muted'] table = [block for block in section.blocks if block[0] == fold.TABLE][0] assert table[1] == ['item', 'checklist row', 'why this job never has one', 'set by'] assert table[2][0][3] == 'plant' body = '\n'.join(block[1] for block in section.blocks if block[0] == fold.MD) assert 'not the job layer' in body assert 'MERGE' in body def test_a_muted_number_that_is_no_checklist_item_opens_the_section(): """It mutes nothing, and nothing is what it looks like it did.""" rows = _mute_rows(('9.9', '(not a checklist item -- this mutes nothing)', 'typed by hand', 'job', True)) section = by_key(rendered(muted={'9.9': 'typed by hand'}, muted_rows=rows))['muted'] assert section.is_open assert 'mute NOTHING' in section.problem def test_the_muted_section_still_renders_from_the_flat_map_alone(): """Every older caller hands over `muted` and no rows. The section must fall back rather than lose the list.""" section = by_key(rendered(muted={'3.3': 'no piece has one'}))['muted'] table = [block for block in section.blocks if block[0] == fold.TABLE][0] assert table[1] == ['item', 'why this job never has one'] assert table[2][0][0] == '3.3' # ------------------------------------------------ the chip on a merged map def test_a_key_the_layers_merge_is_never_chipped_to_one_layer(): """`resolve` names the last layer that set an entry as though it owned all of them. `J` on a map the plant also writes is the same lie as a silent override, one level down.""" given = layers(plant={'checks': {'muted': {'3.3': 'plant'}}}, job={'checks': {'muted': {'5.4': 'job'}}}) row = [r for r in configpage.editable_rows(given) if r['key'] == 'checks.muted'][0] assert row['chip'] == '+' assert row['res']['layer'] == 'plant + job' assert '3.3 (plant)' in row['value'] assert '5.4 (job)' in row['value'] def test_the_legend_explains_the_merged_chip(): """A chip nobody can look up is a chip that reads as a bug.""" assert '`+`' in configpage.LEGEND assert 'MERGE' in configpage.LEGEND def test_a_merged_map_no_layer_sets_still_says_not_set(): given = layers(defaults={}, plant={}, job={}) row = [r for r in configpage.editable_rows(given) if r['key'] == 'checks.muted'][0] assert row['chip'] == '?' assert row['value'] == configpage.NOT_SET