@@ -60,6 +60,58 @@ describe('captureLayoutTree', () => {
6060 expect ( result . root ?. children [ 0 ] ?. tagName ) . toBe ( 'P' ) ;
6161 } ) ;
6262
63+ it ( 'prefers a higher-priority selector even when it matches later in DOM order' , ( ) => {
64+ const doc = createDocument (
65+ '<body><div class="main" data-rect="0,0,1,1">Wrong</div>' +
66+ '<main id="page" data-rect="0,0,800,600">Right</main></body>' ,
67+ ) ;
68+
69+ const result = captureLayoutTree ( null , SELECTORS , FALLBACK_SELECTORS , 10 , doc ) ;
70+
71+ expect ( result . mainSelector ) . toBe ( 'main#page' ) ;
72+ } ) ;
73+
74+ it ( 'prefers a nested higher-priority match over its lower-priority ancestor wrapper' , ( ) => {
75+ const doc = createDocument (
76+ '<body><div class="main" data-rect="0,0,800,600">' +
77+ '<p data-rect="0,0,100,20">breadcrumb</p>' +
78+ '<main id="page" data-rect="0,20,400,580">Real main</main>' +
79+ '<div id="aside" data-rect="400,20,400,580">Sidebar</div>' +
80+ '</div></body>' ,
81+ ) ;
82+
83+ const result = captureLayoutTree ( null , SELECTORS , FALLBACK_SELECTORS , 10 , doc ) ;
84+
85+ expect ( result . mainSelector ) . toBe ( 'main#page' ) ;
86+ } ) ;
87+
88+ it ( 'skips an invalid selector in the priority list and keeps trying the rest' , ( ) => {
89+ const doc = createDocument (
90+ '<body><main id="page" data-rect="0,0,800,600">Right</main></body>' ,
91+ ) ;
92+
93+ const result = captureLayoutTree (
94+ null ,
95+ [ '[[[invalid' , ...SELECTORS ] ,
96+ FALLBACK_SELECTORS ,
97+ 10 ,
98+ doc ,
99+ ) ;
100+
101+ expect ( result . mainSelector ) . toBe ( 'main#page' ) ;
102+ } ) ;
103+
104+ it ( 'prefers the earlier-listed selector when two selectors both match different elements' , ( ) => {
105+ const doc = createDocument (
106+ '<body><div class="main" data-rect="0,0,1,1">Wrong</div>' +
107+ '<div id="main" data-rect="0,0,800,600">Right</div></body>' ,
108+ ) ;
109+
110+ const result = captureLayoutTree ( null , SELECTORS , FALLBACK_SELECTORS , 10 , doc ) ;
111+
112+ expect ( result . mainSelector ) . toBe ( 'div#main' ) ;
113+ } ) ;
114+
63115 it ( 'falls back to the fallback selector list when the priority list has no match' , ( ) => {
64116 const doc = createDocument (
65117 '<body><div id="primaryMain" data-rect="0,0,800,600">Fallback</div></body>' ,
@@ -70,6 +122,26 @@ describe('captureLayoutTree', () => {
70122 expect ( result . mainSelector ) . toBe ( 'div#primaryMain' ) ;
71123 } ) ;
72124
125+ it ( 'still returns DOM-order-first match among fallback selectors (fallback list is not priority-ordered per element)' , ( ) => {
126+ const doc = createDocument (
127+ '<body><div id="wrapperMain" data-rect="0,0,800,600">' +
128+ '<p data-rect="0,0,100,20">breadcrumb</p>' +
129+ '<div id="innerMain" data-rect="0,20,800,580">Inner</div>' +
130+ '</div></body>' ,
131+ ) ;
132+
133+ // Both #wrapperMain and #innerMain match the same fallback selector
134+ // (`[id*="main" i]`), so — unlike the priority list above — there is
135+ // no higher/lower priority to arbitrate between them. `querySelector`
136+ // returns whichever matches first in document order, which is the
137+ // outer wrapper here. This is accepted, existing behavior: the
138+ // priority-list fix only orders *between* selectors in the array,
139+ // not among multiple elements matching the *same* selector.
140+ const result = captureLayoutTree ( null , SELECTORS , FALLBACK_SELECTORS , 10 , doc ) ;
141+
142+ expect ( result . mainSelector ) . toBe ( 'div#wrapperMain' ) ;
143+ } ) ;
144+
73145 it ( 'tries the explicit mainContentSelector before the priority list' , ( ) => {
74146 const doc = createDocument (
75147 '<body><main data-rect="0,0,1,1">Wrong</main><section id="custom" data-rect="0,0,800,600">Right</section></body>' ,
0 commit comments