GitNexus/gitnexus/test/unit/xaml-declarations.test.ts
azizur100389 c629383bd5 Merge remote-tracking branch 'upstream/main' into codex/xaml-support-3202
# Conflicts:
#	gitnexus/src/core/search/fts-schema.ts
2026-09-21 19:05:54 +01:00

121 lines
4.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { extractXamlDeclarations } from '../../src/core/ingestion/documents/xaml.js';
const NS = 'http://schemas.microsoft.com/winfx/2006/xaml';
describe('XAML document declarations (#3202)', () => {
it.each(['\n', '\r\n', '\r'])('preserves source lines with %j line endings', (newline) => {
const source = [
`<Page xmlns:x="${NS}" x:Class="App.Home">`,
' <!-- <Button x:Name="Fake" /> -->',
' <Grid>',
' <Button x:Name="SaveButton" Content="Save" />',
' <Style x:Key="PrimaryButton">',
' <Setter Property="Width" Value="20" />',
' </Style>',
' </Grid>',
'</Page>',
].join(newline);
expect(extractXamlDeclarations(source)).toEqual([
{
name: 'App.Home',
description: 'Page x:Class declaration',
startIndex: 0,
startLine: 0,
endLine: 8,
level: 1,
},
{
name: 'SaveButton',
description: 'Button x:Name declaration',
startIndex: source.replace(/\r\n?/g, '\n').indexOf('<Button x:Name="SaveButton"'),
startLine: 3,
endLine: 3,
level: 3,
},
{
name: 'PrimaryButton',
description: 'Style x:Key declaration',
startIndex: source.replace(/\r\n?/g, '\n').indexOf('<Style'),
startLine: 4,
endLine: 6,
level: 3,
},
]);
});
it('uses namespace identity, including alternate prefixes and scoped rebinding', () => {
const declarations = extractXamlDeclarations(`<Grid xmlns:d="${NS}" xmlns:x="urn:not-xaml">
<Button d:Name="Real" x:Name="WrongNamespace" Name="Unqualified" />
<Panel xmlns:d="urn:other"><Button d:Name="Shadowed" /></Panel>
<Panel xmlns:q="${NS}"><Button q:Name="Nested" /></Panel>
<Button d:Name="AfterScope" />
</Grid>`);
expect(declarations.map((d) => d.name)).toEqual(['Real', 'Nested', 'AfterScope']);
});
it('does not infer bindings, handlers, dynamic resource keys or undeclared x prefixes', () => {
expect(
extractXamlDeclarations(`<Grid xmlns:x="${NS}">
<Button Content="{Binding User}" Click="Save" />
<Style x:Key="{x:Type Button}" />
<Button x:Name="" /><Style x:Key="A&amp;B" />
<![CDATA[<Button x:Name="Fake" />]]>
</Grid>`),
).toEqual([]);
expect(extractXamlDeclarations('<Button x:Name="Undeclared" />')).toEqual([]);
});
it('does not mistake DOCTYPE text inside CDATA, comments, or processing instructions for a DTD', () => {
const declarations = extractXamlDeclarations(`<Grid xmlns:x="${NS}" x:Name="ActualView">
<![CDATA[<!DOCTYPE example>]]>
<!-- <!DOCTYPE example> -->
<?example <!DOCTYPE example>?>
</Grid>`);
expect(declarations).toEqual([
expect.objectContaining({ name: 'ActualView', description: 'Grid x:Name declaration' }),
]);
});
it.each([
['{}escaped', 'escaped'],
['{}{x:Type Button}', '{x:Type Button}'],
['{}{}nested', '{}nested'],
['{}left{right}', 'left{right}'],
])('indexes escaped literal resource key %s as %s', (value, name) => {
const source = `<Style xmlns:q="${NS}" q:Key="${value}" />`;
expect(extractXamlDeclarations(source)).toEqual([
{
name,
description: 'Style x:Key declaration',
startIndex: 0,
startLine: 0,
endLine: 0,
level: 1,
},
]);
});
it.each(['{}', '{} ', '{}A&amp;B', '{}&#123;key'])(
'does not index empty or unresolved entity values after an escape: %s',
(value) => {
expect(extractXamlDeclarations(`<Style xmlns:x="${NS}" x:Key="${value}" />`)).toEqual([]);
},
);
it('keeps duplicate names distinct and preserves Unicode names', () => {
const declarations = extractXamlDeclarations(
`<Grid xmlns:x="${NS}"><Button x:Name="Save" /><Button x:Name="Save" /><Button x:Name="\u03a9" /></Grid>`,
);
expect(declarations.map((d) => d.name)).toEqual(['Save', 'Save', '\u03a9']);
expect(new Set(declarations.map((d) => d.startIndex)).size).toBe(3);
});
it.each([
'<Grid><Button></Grid>',
`<!DOCTYPE Grid [<!ENTITY label SYSTEM "file:///private">]><Grid xmlns:x="${NS}" x:Name="&label;" />`,
`${'<Grid>'.repeat(101)}${'</Grid>'.repeat(101)}`,
])('rejects malformed or unsupported input without partial declarations', (source) => {
expect(() => extractXamlDeclarations(source)).toThrow();
});
});