Skip to content

Commit 361c32c

Browse files
committed
Add attributes list page
1 parent 3107a6b commit 361c32c

7 files changed

Lines changed: 114 additions & 2 deletions

File tree

.vitepress/config.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ gtag('config', 'G-VYGDN3X0PR');`],
104104
{
105105
text: 'Guide',
106106
items: [
107+
{ text: 'All Attributes', link: '/docs/attributes.md' },
107108
{ text: 'CLI Reference', link: '/docs/cli-reference.md' },
108109
{ text: 'AI Agents', link: '/docs/ai-agents.md' },
109110
],
@@ -168,6 +169,7 @@ gtag('config', 'G-VYGDN3X0PR');`],
168169
{
169170
text: 'Руководство',
170171
items: [
172+
{ text: 'Все атрибуты', link: '/ru/docs/attributes.md' },
171173
{ text: 'CLI справка', link: '/ru/docs/cli-reference.md' },
172174
{ text: 'AI-агенты', link: '/ru/docs/ai-agents.md' },
173175
],

.vitepress/plugin-block.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ export function getPluginEntry(localeCode: string, name: string): PluginRegistry
7373
return registry.get(localeCode)?.get(name.toLowerCase())
7474
}
7575

76+
export function getPluginByPagePath(localeCode: string, pagePath: string): PluginRegistryEntry | undefined {
77+
const map = registry.get(localeCode)
78+
if (!map) return undefined
79+
for (const entry of map.values()) {
80+
if (entry.pagePath === pagePath) return entry
81+
}
82+
return undefined
83+
}
84+
7685
// ─── Pre-scan ────────────────────────────────────────────
7786

7887
/**

.vitepress/signature-registry.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,14 @@ export function preScanSignatures(srcDir: string): void {
254254
}
255255
}
256256

257+
/**
258+
* Return all attribute entries for a locale.
259+
*/
260+
export function getAllAttrEntries(localeCode: string): RegistryEntry[] {
261+
const map = attrRegistry.get(localeCode)
262+
return map ? [...map.values()] : []
263+
}
264+
257265
/**
258266
* Look up a function signature entry by FQN and locale.
259267
*/

.vitepress/signature.ts

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import type MarkdownIt from 'markdown-it'
1212
import type StateInline from 'markdown-it/lib/rules_inline/state_inline.mjs'
1313
import type StateBlock from 'markdown-it/lib/rules_block/state_block.mjs'
1414
import { getLocaleByPath, type LocaleConfig } from './locales'
15-
import { getEntry, getAttrEntry, getClassEntry, getEnumCaseEntry } from './signature-registry'
16-
import { getPluginEntry } from './plugin-block'
15+
import { getEntry, getAttrEntry, getClassEntry, getEnumCaseEntry, getAllAttrEntries, type RegistryEntry } from './signature-registry'
16+
import { getPluginEntry, getPluginByPagePath } from './plugin-block'
1717

1818
interface Param {
1919
name: string
@@ -377,6 +377,28 @@ export function funcBlockPlugin(md: MarkdownIt) {
377377
return renderEnumRefHtml(md, rawFqn, locale)
378378
})
379379

380+
// ── <attributes-list /> block rule ──
381+
382+
md.block.ruler.before('html_block', 'attributes_list', (state, startLine, _endLine, silent) => {
383+
const pos = state.bMarks[startLine] + state.tShift[startLine]
384+
const max = state.eMarks[startLine]
385+
const line = state.src.slice(pos, max).trim()
386+
387+
if (line !== '<attributes-list />' && line !== '<attributes-list/>') return false
388+
if (silent) return true
389+
390+
const token = state.push('attributes_list', '', 0)
391+
const relativePath = state.env?.relativePath || ''
392+
token.meta = { locale: getLocaleByPath('/' + relativePath) }
393+
state.line = startLine + 1
394+
return true
395+
})
396+
397+
md.renderer.rules['attributes_list'] = (tokens, idx) => {
398+
const locale = tokens[idx].meta?.locale
399+
return renderAttributesList(md, locale)
400+
}
401+
380402
// ── <signature> block rule (multi-line) ──
381403

382404
md.block.ruler.before('html_block', 'func_block', (state, startLine, endLine, silent) => {
@@ -756,3 +778,58 @@ function renderEnumBlock(md: MarkdownIt, data: EnumRenderData, env?: any): strin
756778
html += '</div>\n'
757779
return html
758780
}
781+
782+
// ─── <attributes-list /> renderer ───────────────────────
783+
784+
function renderAttributesList(md: MarkdownIt, locale?: LocaleConfig): string {
785+
const localeCode = locale?.code ?? 'en'
786+
const entries = getAllAttrEntries(localeCode)
787+
788+
if (entries.length === 0) return ''
789+
790+
// Group by pagePath
791+
const groups = new Map<string, RegistryEntry[]>()
792+
for (const entry of entries) {
793+
const list = groups.get(entry.pagePath) || []
794+
list.push(entry)
795+
groups.set(entry.pagePath, list)
796+
}
797+
798+
const tableLabels: Record<string, { attr: string; desc: string }> = {
799+
en: { attr: 'Attribute', desc: 'Description' },
800+
ru: { attr: 'Атрибут', desc: 'Описание' },
801+
}
802+
const l = tableLabels[localeCode] ?? tableLabels.en
803+
804+
// Sort groups by plugin name
805+
const sortedGroups = [...groups.entries()].sort((a, b) => {
806+
const nameA = getPluginByPagePath(localeCode, a[0])?.name ?? a[0]
807+
const nameB = getPluginByPagePath(localeCode, b[0])?.name ?? b[0]
808+
return nameA.localeCompare(nameB)
809+
})
810+
811+
let html = ''
812+
813+
for (const [pagePath, attrs] of sortedGroups) {
814+
const plugin = getPluginByPagePath(localeCode, pagePath)
815+
const groupName = plugin?.name ?? pagePath.split('/').pop() ?? 'Unknown'
816+
const slug = 'attrs-' + groupName.toLowerCase().replace(/\s+/g, '-')
817+
818+
if (plugin) {
819+
const pluginHtml = renderPluginRefHtml(plugin.name, locale)
820+
html += `<h2 id="${escapeHtml(slug)}">${pluginHtml}</h2>\n`
821+
} else {
822+
html += `<h2 id="${escapeHtml(slug)}">${escapeHtml(groupName)}</h2>\n`
823+
}
824+
825+
html += `<table>\n<thead><tr><th>${escapeHtml(l.attr)}</th><th>${escapeHtml(l.desc)}</th></tr></thead>\n<tbody>\n`
826+
for (const attr of attrs) {
827+
const attrHtml = renderAttrRefHtml(md, attr.fqn, locale)
828+
const shortHtml = attr.short ? md.renderInline(attr.short) : ''
829+
html += `<tr><td>${attrHtml}</td><td>${shortHtml}</td></tr>\n`
830+
}
831+
html += '</tbody>\n</table>\n'
832+
}
833+
834+
return html
835+
}

docs/attributes.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
llms_description: "Complete reference of all PHP attributes in Testo: #[Test], #[Retry], #[Bench], lifecycle hooks (#[BeforeTest], #[AfterTest], #[BeforeClass], #[AfterClass]), data providers (#[DataSet], #[DataProvider], #[DataZip], #[DataCross], #[DataUnion]), #[TestInline], coverage (#[Covers], #[CoversNothing])."
3+
---
4+
5+
# All Attributes
6+
7+
Testo uses PHP attributes to configure tests, lifecycle hooks, data providers, and more. This page lists all available attributes grouped by plugin. Click any attribute name for full documentation with parameters and examples.
8+
9+
<attributes-list />

docs/plugins/data.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ llms_description: "Parameterized tests with data providers. #[DataSet] for inlin
66

77
Data providers let you run one test with different sets of input data. Each set runs as a separate test.
88

9+
<plugin-info name="Data" />
10+
911
<signature h="2" name="#[\Testo\Data\DataSet(array $arguments, ?string $name = null)]">
1012
<short>Declares a set of arguments for a parameterized test. Can be used multiple times — each attribute creates a separate test run.</short>
1113
<param name="$arguments">Array of values passed to the test method.</param>

ru/docs/attributes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Все атрибуты
2+
3+
Testo использует PHP-атрибуты для настройки тестов, хуков жизненного цикла, провайдеров данных и многого другого. На этой странице собраны все доступные атрибуты, сгруппированные по плагинам. Нажмите на название атрибута, чтобы перейти к полной документации с параметрами и примерами.
4+
5+
<attributes-list />

0 commit comments

Comments
 (0)