@@ -208,3 +208,77 @@ exit code 1
208208 assert . equal ( out . selectorKind , 'id' ) ;
209209 assert . equal ( out . selector , 'fab-create-task' ) ;
210210} ) ;
211+
212+ // ─────────────────────────────────────────────────────────────────────────────
213+ // GH #105 / B152: maestro-runner 1.0.9 stderr shape
214+ // ─────────────────────────────────────────────────────────────────────────────
215+ // The shape `Element not found: id='X'` (with colon + equals) is emitted by
216+ // maestro-runner 1.0.9+. Before PR #159 the parser only recognized the
217+ // classic `Element with id 'X' not found` shape and returned UNKNOWN for
218+ // the modern form — silently disabling the L3 self-healing loop. These
219+ // tests pin the new patterns so a regression would be immediately visible.
220+
221+ test ( 'parser: 1.0.9 shape — id=\'X\' single-quoted' , ( ) => {
222+ const out = parseMaestroFailure ( " ✗ tapOn: id=\"task-mark-all-done\" (12.7s)\n ╰─ Element not found: id='task-mark-all-done'\n" ) ;
223+ assert . equal ( out . kind , 'SELECTOR_NOT_FOUND' ) ;
224+ assert . equal ( out . selectorKind , 'id' ) ;
225+ assert . equal ( out . selector , 'task-mark-all-done' ) ;
226+ } ) ;
227+
228+ test ( 'parser: 1.0.9 shape — id="X" double-quoted' , ( ) => {
229+ const out = parseMaestroFailure ( 'Element not found: id="btn-submit"' ) ;
230+ assert . equal ( out . kind , 'SELECTOR_NOT_FOUND' ) ;
231+ assert . equal ( out . selectorKind , 'id' ) ;
232+ assert . equal ( out . selector , 'btn-submit' ) ;
233+ } ) ;
234+
235+ test ( 'parser: 1.0.9 shape — text=\'X\' single-quoted' , ( ) => {
236+ const out = parseMaestroFailure ( "Element not found: text='All done'" ) ;
237+ assert . equal ( out . kind , 'SELECTOR_NOT_FOUND' ) ;
238+ assert . equal ( out . selectorKind , 'text' ) ;
239+ assert . equal ( out . selector , 'All done' ) ;
240+ } ) ;
241+
242+ test ( 'parser: 1.0.9 shape — extra whitespace between : and id= tolerated' , ( ) => {
243+ // maestro-runner formatting could insert any amount of whitespace; the
244+ // pattern uses \s* so this must match.
245+ const out = parseMaestroFailure ( "Element not found: id='spinner'" ) ;
246+ assert . equal ( out . kind , 'SELECTOR_NOT_FOUND' ) ;
247+ assert . equal ( out . selector , 'spinner' ) ;
248+ } ) ;
249+
250+ test ( 'parser: 1.0.9 shape — embedded opposite quote in id (e.g. user\\\'s-tasks)' , ( ) => {
251+ // Matched-quote backreference pattern allows the opposite quote inside
252+ // the value. Same invariant we test for the classic shape (line 176-189).
253+ const out = parseMaestroFailure ( `Element not found: id="say-'hi'-btn"` ) ;
254+ assert . equal ( out . kind , 'SELECTOR_NOT_FOUND' ) ;
255+ assert . equal ( out . selector , "say-'hi'-btn" ) ;
256+ } ) ;
257+
258+ test ( 'parser: 1.0.9 shape — full realistic maestro-runner 1.0.9 stderr' , ( ) => {
259+ // Captured verbatim from the #105 MTTR experiment session. The earlier
260+ // classic patterns must NOT match (no "Element with id 'X' not found"
261+ // string), but the new 1.0.9 patterns must classify correctly.
262+ const realistic = `maestro-runner 1.0.9 - by DeviceLab.dev
263+ ✓ launchApp (2.3s)
264+ ✓ tapOn: id="tab-tasks" (2.8s)
265+ ✓ assertVisible: text="Tasks" (1.3s)
266+ ✗ tapOn: id="task-mark-all-done" (12.7s)
267+ ╰─ Element not found: id='task-mark-all-done'
268+ 3 steps passing
269+ 1 steps failing
270+ ✗ rn-maestro-run 23.8s` ;
271+ const out = parseMaestroFailure ( realistic ) ;
272+ assert . equal ( out . kind , 'SELECTOR_NOT_FOUND' , `expected SELECTOR_NOT_FOUND, got ${ out . kind } — the 1.0.9 pattern MUST match this verbatim stderr` ) ;
273+ assert . equal ( out . selectorKind , 'id' ) ;
274+ assert . equal ( out . selector , 'task-mark-all-done' ) ;
275+ } ) ;
276+
277+ test ( 'parser: 1.0.9 id= shape has priority over the generic fallback' , ( ) => {
278+ // The 1.0.9 id= pattern is more specific than the catch-all "Element 'X' not found".
279+ // Pattern order in maestro-error-parser.ts MUST keep id-shape ahead of the fallback.
280+ // If a regression reorders patterns, this test catches it.
281+ const out = parseMaestroFailure ( "Element not found: id='specific-id'\nAlso seen: Element 'fallback-id' not found" ) ;
282+ assert . equal ( out . selectorKind , 'id' , 'id= shape must win over fallback when both present' ) ;
283+ assert . equal ( out . selector , 'specific-id' ) ;
284+ } ) ;
0 commit comments