instance()->getTable(); } /** * @return list> */ function baselineCompareEvidenceGapBuckets(): array { return BaselineCompareEvidenceGapDetails::fromContext(BaselineSubjectResolutionFixtures::compareContext([ BaselineSubjectResolutionFixtures::structuredGap([ 'policy_type' => 'deviceConfiguration', 'subject_key' => 'WiFi-Corp-Profile', 'subject_class' => 'policy_backed', 'resolution_path' => 'policy', 'resolution_outcome' => 'ambiguous_match', 'reason_code' => 'ambiguous_match', 'operator_action_category' => 'inspect_subject_mapping', ]), BaselineSubjectResolutionFixtures::structuredGap([ 'policy_type' => 'deviceConfiguration', 'subject_key' => 'VPN-Always-On', 'subject_class' => 'policy_backed', 'resolution_path' => 'policy', 'resolution_outcome' => 'ambiguous_match', 'reason_code' => 'ambiguous_match', 'operator_action_category' => 'inspect_subject_mapping', ]), BaselineSubjectResolutionFixtures::structuredGap([ 'policy_type' => 'deviceCompliancePolicy', 'subject_key' => 'Windows-Encryption-Required', 'subject_class' => 'policy_backed', 'resolution_path' => 'policy', 'resolution_outcome' => 'ambiguous_match', 'reason_code' => 'ambiguous_match', 'operator_action_category' => 'inspect_subject_mapping', ]), BaselineSubjectResolutionFixtures::structuredGap([ 'policy_type' => 'deviceConfiguration', 'subject_key' => 'Deleted-Policy-ABC', 'resolution_outcome' => 'policy_record_missing', 'reason_code' => 'policy_record_missing', 'operator_action_category' => 'run_policy_sync_or_backup', ]), BaselineSubjectResolutionFixtures::structuredGap([ 'policy_type' => 'deviceCompliancePolicy', 'subject_key' => 'Retired-Compliance-Policy', 'resolution_outcome' => 'policy_record_missing', 'reason_code' => 'policy_record_missing', 'operator_action_category' => 'run_policy_sync_or_backup', ]), ]))['buckets']; } it('uses a Filament table for evidence-gap rows with searchable visible columns', function (): void { $component = Livewire::test(BaselineCompareEvidenceGapTable::class, [ 'buckets' => baselineCompareEvidenceGapBuckets(), 'context' => 'canonical-run', ]); $table = baselineCompareEvidenceGapTable($component); expect($table->isSearchable())->toBeTrue(); expect($table->getDefaultSortColumn())->toBe('reason_label'); expect($table->getColumn('reason_label')?->isSearchable())->toBeTrue(); expect($table->getColumn('policy_type')?->isSearchable())->toBeTrue(); expect($table->getColumn('subject_class_label')?->isSearchable())->toBeTrue(); expect($table->getColumn('resolution_outcome_label')?->isSearchable())->toBeTrue(); expect($table->getColumn('operator_action_category_label')?->isSearchable())->toBeTrue(); expect($table->getColumn('subject_key')?->isSearchable())->toBeTrue(); $component ->assertSee('WiFi-Corp-Profile') ->assertSee('Deleted-Policy-ABC') ->assertSee('Reason') ->assertSee('Policy type') ->assertSee('Subject class') ->assertSee('Outcome') ->assertSee('Next action') ->assertSee('Subject key') ->assertSee('Policy-backed') ->assertSee('Policy record missing') ->assertSee('Run policy sync or backup') ->assertSee(TagBadgeCatalog::spec(TagBadgeDomain::PolicyType, 'deviceConfiguration')->label) ->assertSee(TagBadgeCatalog::spec(TagBadgeDomain::PolicyType, 'deviceCompliancePolicy')->label); }); it('filters evidence-gap rows by table search and select filters', function (): void { Livewire::test(BaselineCompareEvidenceGapTable::class, [ 'buckets' => baselineCompareEvidenceGapBuckets(), 'context' => 'tenant-landing', ]) ->searchTable('Deleted-Policy-ABC') ->assertSee('Deleted-Policy-ABC') ->assertDontSee('WiFi-Corp-Profile'); Livewire::test(BaselineCompareEvidenceGapTable::class, [ 'buckets' => baselineCompareEvidenceGapBuckets(), 'context' => 'tenant-landing-filters', ]) ->filterTable('reason_code', 'policy_record_missing') ->assertSee('Retired-Compliance-Policy') ->assertDontSee('VPN-Always-On') ->filterTable('policy_type', 'deviceCompliancePolicy') ->assertSee('Retired-Compliance-Policy') ->assertDontSee('Deleted-Policy-ABC') ->filterTable('operator_action_category', 'run_policy_sync_or_backup') ->assertSee('Run policy sync or backup') ->filterTable('subject_class', 'policy_backed') ->assertSee('Policy-backed'); }); it('shows an explicit empty state when only missing-detail buckets exist', function (): void { $buckets = BaselineCompareEvidenceGapDetails::fromContext([ 'baseline_compare' => [ 'evidence_gaps' => [ 'count' => 2, 'by_reason' => [ 'policy_record_missing' => 2, ], 'subjects' => [], ], ], ])['buckets']; Livewire::test(BaselineCompareEvidenceGapTable::class, [ 'buckets' => $buckets, 'context' => 'legacy-run', ]) ->assertSee('No recorded gap rows match this view') ->assertSee('Adjust the current search or filters to review other affected subjects.'); });