[self::R_PERSON_SEE, self::R_PERSON_UPDATE], 'Report' => [self::R_REPORT_SEE], ]; } }; $roleProvider = new RoleProvider([$provider]); // Fake role hierarchy: UPDATE implies SEE; others none $roleHierarchy = new class () implements RoleHierarchyInterface { public function getReachableRoleNames(array $roles): array { $output = []; foreach ($roles as $r) { $output[] = $r; if ('CHILL_PERSON_UPDATE' === $r) { $output[] = 'CHILL_PERSON_SEE'; } } return array_values(array_unique($output)); } }; // Fake translator that clearly shows translation applied $translator = new class () implements TranslatorInterface { public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string { return 'T('.$id.')'; } public function getLocale(): string { return 'en'; } }; $dumper = new RoleDumper($roleProvider, $roleHierarchy, $translator); $md = $dumper->dumpAsMarkdown(); $expected = "## T(Person)\n" ."- **T(CHILL_PERSON_SEE)** (S)\n" ."- **T(CHILL_PERSON_UPDATE)** (S): T(CHILL_PERSON_SEE)\n\n" ."## T(Report)\n" ."- **T(CHILL_REPORT_SEE)** (~~S~~)\n"; self::assertSame($expected, $md); } }