| 1 |
// buildAgentBlockDescriptor builds the agent-block selection envelope |
| 2 |
// that powers Agent workflow picking. DOMHighlighter (click) and |
| 3 |
// ask-ai (Ask AI pill) both call this — the test pins the shape so |
| 4 |
// both surfaces resolve metadata identically. |
| 5 |
|
| 6 |
import { buildAgentBlockDescriptor } from '@quick-edit/lib/agent-block-descriptor'; |
| 7 |
|
| 8 |
const makeBlock = ({ html = '', tagName = 'section', classes = [] } = {}) => { |
| 9 |
const el = document.createElement(tagName); |
| 10 |
el.setAttribute('data-extendify-agent-block-id', 'b-1'); |
| 11 |
for (const c of classes) el.classList.add(c); |
| 12 |
if (html) el.innerHTML = html; |
| 13 |
return el; |
| 14 |
}; |
| 15 |
|
| 16 |
describe('buildAgentBlockDescriptor', () => { |
| 17 |
it('returns null when no element is given', () => { |
| 18 |
expect(buildAgentBlockDescriptor(null)).toBeNull(); |
| 19 |
expect(buildAgentBlockDescriptor(undefined)).toBeNull(); |
| 20 |
}); |
| 21 |
|
| 22 |
it('reads id and target from data-extendify-agent-block-id', () => { |
| 23 |
const el = makeBlock(); |
| 24 |
const d = buildAgentBlockDescriptor(el); |
| 25 |
expect(d.id).toBe('b-1'); |
| 26 |
expect(d.target).toBe('data-extendify-agent-block-id'); |
| 27 |
expect(d.template).toBeUndefined(); |
| 28 |
}); |
| 29 |
|
| 30 |
it('detects hasText from non-whitespace text content', () => { |
| 31 |
const withText = makeBlock(); |
| 32 |
withText.textContent = 'hello'; |
| 33 |
expect(buildAgentBlockDescriptor(withText).hasText).toBe(true); |
| 34 |
|
| 35 |
const blank = makeBlock(); |
| 36 |
blank.textContent = ' \n\t'; |
| 37 |
expect(buildAgentBlockDescriptor(blank).hasText).toBe(false); |
| 38 |
}); |
| 39 |
|
| 40 |
it('treats zero-width-space content as no text', () => { |
| 41 |
const el = makeBlock(); |
| 42 |
el.textContent = ''; |
| 43 |
expect(buildAgentBlockDescriptor(el).hasText).toBe(false); |
| 44 |
}); |
| 45 |
|
| 46 |
it('detects hasLinks from descendant or self anchor', () => { |
| 47 |
const withChild = makeBlock({ html: '<a href="/">x</a>' }); |
| 48 |
expect(buildAgentBlockDescriptor(withChild).hasLinks).toBe(true); |
| 49 |
|
| 50 |
const selfAnchor = document.createElement('a'); |
| 51 |
selfAnchor.setAttribute('data-extendify-agent-block-id', 'b-2'); |
| 52 |
expect(buildAgentBlockDescriptor(selfAnchor).hasLinks).toBe(true); |
| 53 |
|
| 54 |
const none = makeBlock(); |
| 55 |
expect(buildAgentBlockDescriptor(none).hasLinks).toBe(false); |
| 56 |
}); |
| 57 |
|
| 58 |
it('detects hasImages from .wp-block-image, img tags, and self class', () => { |
| 59 |
const wpBlock = makeBlock({ html: '<div class="wp-block-image"></div>' }); |
| 60 |
expect(buildAgentBlockDescriptor(wpBlock).hasImages).toBe(true); |
| 61 |
|
| 62 |
const innerImg = makeBlock({ html: '<img src="x" />' }); |
| 63 |
expect(buildAgentBlockDescriptor(innerImg).hasImages).toBe(true); |
| 64 |
|
| 65 |
const selfWpImage = makeBlock({ classes: ['wp-block-image'] }); |
| 66 |
expect(buildAgentBlockDescriptor(selfWpImage).hasImages).toBe(true); |
| 67 |
|
| 68 |
const none = makeBlock(); |
| 69 |
expect(buildAgentBlockDescriptor(none).hasImages).toBe(false); |
| 70 |
}); |
| 71 |
|
| 72 |
it('detects hasNav / hasSiteTitle / hasSiteLogo from class or descendant class', () => { |
| 73 |
const allInside = makeBlock({ |
| 74 |
html: ` |
| 75 |
<nav class="wp-block-navigation"></nav> |
| 76 |
<h1 class="wp-block-site-title">Site</h1> |
| 77 |
<div class="wp-block-site-logo"></div> |
| 78 |
`, |
| 79 |
}); |
| 80 |
const d = buildAgentBlockDescriptor(allInside); |
| 81 |
expect(d.hasNav).toBe(true); |
| 82 |
expect(d.hasSiteTitle).toBe(true); |
| 83 |
expect(d.hasSiteLogo).toBe(true); |
| 84 |
|
| 85 |
const navSelf = makeBlock({ classes: ['wp-block-navigation'] }); |
| 86 |
expect(buildAgentBlockDescriptor(navSelf).hasNav).toBe(true); |
| 87 |
|
| 88 |
const titleSelf = makeBlock({ classes: ['wp-block-site-title'] }); |
| 89 |
expect(buildAgentBlockDescriptor(titleSelf).hasSiteTitle).toBe(true); |
| 90 |
|
| 91 |
const logoSelf = makeBlock({ classes: ['wp-block-site-logo'] }); |
| 92 |
expect(buildAgentBlockDescriptor(logoSelf).hasSiteLogo).toBe(true); |
| 93 |
|
| 94 |
const none = makeBlock(); |
| 95 |
expect(none).toBeDefined(); |
| 96 |
const d2 = buildAgentBlockDescriptor(none); |
| 97 |
expect(d2.hasNav).toBe(false); |
| 98 |
expect(d2.hasSiteTitle).toBe(false); |
| 99 |
expect(d2.hasSiteLogo).toBe(false); |
| 100 |
}); |
| 101 |
|
| 102 |
it('promotes the template-part ancestor: id, target, and template slug', () => { |
| 103 |
const part = document.createElement('header'); |
| 104 |
part.setAttribute('data-extendify-part', 'header'); |
| 105 |
part.setAttribute('data-extendify-part-block-id', 'part-7'); |
| 106 |
const inner = makeBlock(); |
| 107 |
inner.textContent = 'logo+nav'; |
| 108 |
part.appendChild(inner); |
| 109 |
|
| 110 |
const d = buildAgentBlockDescriptor(inner); |
| 111 |
expect(d.id).toBe('part-7'); |
| 112 |
expect(d.target).toBe('data-extendify-part-block-id'); |
| 113 |
expect(d.template).toBe('header'); |
| 114 |
expect(d.hasText).toBe(true); |
| 115 |
}); |
| 116 |
}); |
| 117 |
|