`;
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'contains-html' },
outputString: complexHtml,
inverse: false,
};
const result = handleContainsHtml(params);
expect(result.pass).toBe(true);
expect(result.score).toBe(1);
});
it('should handle edge cases with minimal HTML indicators', () => {
// These should pass - have multiple HTML indicators
const minimalHtmlPasses = [
'
', // tag + entity
'text', // comment + tag
' ', // multiple self-closing tags
'Text with bold and italic', // multiple paired tags
];
minimalHtmlPasses.forEach((html) => {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'contains-html' },
outputString: html,
inverse: false,
};
const result = handleContainsHtml(params);
expect(result.pass).toBe(true);
});
// These should fail - only one HTML indicator
const minimalHtmlFails = [
'', // single unpaired non-standard tag
'&', // just an entity
'', // just a comment
];
minimalHtmlFails.forEach((text) => {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'contains-html' },
outputString: text,
inverse: false,
};
const result = handleContainsHtml(params);
expect(result.pass).toBe(false);
});
});
});
describe('handleIsHtml', () => {
it('should pass when output is valid HTML', () => {
const validHtmlExamples = [
'
Hello World
',
'TestContent',
'
Simple paragraph
',
'
Nested
',
'',
' ',
'
Content
',
'
With whitespace
',
];
validHtmlExamples.forEach((html) => {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'is-html' },
outputString: html,
inverse: false,
};
const result = handleIsHtml(params);
expect(result.pass).toBe(true);
expect(result.score).toBe(1);
});
});
it('should fail when output is not valid HTML', () => {
const invalidHtmlExamples = [
{ output: 'Just plain text', reason: 'Output must be wrapped in HTML tags' },
{
output: 'Some text with HTML inside',
reason: 'Output must be wrapped in HTML tags',
},
{
output: 'Text before
HTML
',
reason: 'Output must be wrapped in HTML tags',
},
{ output: '
HTML
Text after', reason: 'Output must be wrapped in HTML tags' },
{
output: 'XML',
reason: 'Output appears to be XML, not HTML',
},
{ output: '', reason: 'Output is empty' },
{ output: ' ', reason: 'Output is empty' },
{ output: '', reason: 'Output does not contain recognized HTML elements' },
{ output: '2 < 3 and 4 > 1', reason: 'Output must be wrapped in HTML tags' },
];
invalidHtmlExamples.forEach(({ output, reason }) => {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'is-html' },
outputString: output,
inverse: false,
};
const result = handleIsHtml(params);
expect(result.pass).toBe(false);
expect(result.score).toBe(0);
expect(result.reason).toBe(reason);
});
});
it('should handle inverse assertion correctly', () => {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'not-is-html' },
outputString: 'Just plain text',
inverse: true,
};
const result = handleIsHtml(params);
expect(result).toEqual({
pass: true,
score: 1,
reason: 'Assertion passed',
assertion: params.assertion,
});
});
it('should handle inverse assertion when HTML is present', () => {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'not-is-html' },
outputString: '
out to per HTML5 spec,
// so the wrapper check must still catch it.
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'is-html' },
outputString: '
stray text
x
',
inverse: false,
};
const result = handleIsHtml(params);
expect(result.pass).toBe(false);
expect(result.reason).toBe('Output must be wrapped in HTML tags');
});
it('should reject unknown tags that merely share a prefix with html/head/body', () => {
// Regression: a substring check like input.includes('`, causing the auto-injected to be treated as
// user-provided and the assertion to incorrectly pass.
const prefixCollisions = [
{ input: 'plain text', tag: 'headphones' },
{ input: 'x', tag: 'bodyguard' },
{ input: 'x', tag: 'htmlworld' },
{ input: 'x', tag: 'headd' },
];
prefixCollisions.forEach(({ input }) => {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'is-html' },
outputString: input,
inverse: false,
};
const result = handleIsHtml(params);
expect(result.pass).toBe(false);
expect(result.reason).toBe('Output does not contain recognized HTML elements');
});
});
it('should still accept body variants regardless of trailing chars', () => {
// Control cases for the wrapper detection fix — body followed by `>`,
// whitespace, or `/` must continue to count as user-declared body.
const bodyVariants = ['x', 'x', '
'];
bodyVariants.forEach((input) => {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'is-html' },
outputString: input,
inverse: false,
};
const result = handleIsHtml(params);
expect(result.pass).toBe(true);
});
});
it('should reject incomplete or ignored wrapper tags', () => {
const invalidWrappers = [
{ input: '', reason: 'Output must be wrapped in HTML tags' },
{ input: 'plain {
const params: AssertionParams = {
...defaultParams,
assertion: { type: 'is-html' },
outputString: input,
inverse: false,
};
const result = handleIsHtml(params);
expect(result.pass).toBe(false);
expect(result.reason).toBe(reason);
});
});
it('should handle pathologically deep nesting without stack overflow', () => {
// Nesting 15k unknown elements forces the iterative walker to actually
// traverse the full depth: unknown tags fail `isUserProvidedElement`, so
// `findFirstElement` cannot short-circuit until it reaches the
at
// the leaf. A recursive walker blows V8's ~10-15k stack-frame limit here.
const depth = 15_000;
const deeplyNested = `${''.repeat(depth)}