{"record":{"id":"8e7cb363006ec341","repo":"tastejs/todomvc","slug":"cannot-determine-what-kind-of-selectors-this-app-u","errorCode":null,"errorMessage":"Cannot determine what kind of selectors this app uses. Add it to usesIDSelectors.","messagePattern":"Cannot determine what kind of selectors this app uses\\. Add it to usesIDSelectors\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cypress/e2e/spec.cy.js","lineNumber":501,"sourceCode":"      // Walk the document INCLUDING shadow roots — web-component apps\n      // (lit, polymer) keep their UI inside a shadow tree, so a plain\n      // doc.querySelector misses them. Returns the first element that\n      // matches `selector` anywhere in the light or shadow DOM.\n      const findDeep = (root, selector) => {\n        if (!root) return false\n        if (root.querySelector?.(selector)) return true\n        const candidates = root.querySelectorAll?.('*') || []\n        for (const el of candidates) {\n          if (el.shadowRoot && findDeep(el.shadowRoot, selector)) return true\n        }\n        return false\n      }\n      cy.document().then(doc => {\n        if (framework in usesIDSelectors) {\n          setSelectors(usesIDSelectors[framework])\n          createTodoCommands(usesIDSelectors[framework])\n        } else if (findDeep(doc, 'input#new-todo') && findDeep(doc, 'input.new-todo')) {\n          throw new Error(\n            'Cannot determine what kind of selectors this app uses. Add it to usesIDSelectors.'\n          )\n        } else if (findDeep(doc, 'input#new-todo')) {\n          cy.log('app uses ID selectors')\n          setSelectors(true)\n          createTodoCommands(true)\n        } else if (findDeep(doc, 'input.new-todo')) {\n          cy.log('app uses class selectors')\n          setSelectors(false)\n          createTodoCommands(false)\n        } else {\n          throw new Error(\n            'Cannot determine what kind of selectors this app uses.'\n          )\n        }\n      })\n    })\n","sourceCodeStart":483,"sourceCodeEnd":519,"githubUrl":"https://github.com/tastejs/todomvc/blob/ff43b02e59dfa604386bb382034b2cd07c2bcd8a/cypress/e2e/spec.cy.js#L483-L519","documentation":"Thrown at cypress/e2e/spec.cy.js:501 during selector-style auto-detection in the setup hook. The spec decides whether to drive the app with ID selectors (`input#new-todo`) or class selectors (`input.new-todo`) by probing the live document (including shadow roots, via findDeep at line 487). Frameworks listed in the `usesIDSelectors` map (line 132, currently only `polymer: false`) bypass detection; for every other framework it inspects the DOM. If BOTH `input#new-todo` AND `input.new-todo` are present the choice is ambiguous, so it throws and asks you to register the framework explicitly rather than guess.","triggerScenarios":"Running against a framework that is NOT a key in `usesIDSelectors` (anything other than `polymer`) whose rendered DOM contains both an element matching `input#new-todo` and an element matching `input.new-todo`. This commonly happens when a single input carries both id=\"new-todo\" and class=\"new-todo\", or when the app renders two inputs.","commonSituations":"Adding a new TodoMVC example whose template assigns both the id and the class to the new-todo input; a framework upgrade that changes the markup to include both; a shadow-DOM web-component app (lit/polyderivative) whose shadow tree exposes both selectors. The map at line 132 only whitelists `polymer`, so most real apps fall through to runtime detection.","solutions":["Register the framework in the `usesIDSelectors` map at line 132, e.g. `yourframework: true` if the app uses IDs or `yourframework: false` if it uses classes — this short-circuits detection at line 497.","Inspect the rendered DOM (`cy.document().then(d => cy.log(d.querySelector('input#new-todo'), d.querySelector('input.new-todo')))`) to decide which value (true/false) to register.","If the dual-selector is unintentional, fix the app template so it exposes only one of the two conventions."],"exampleFix":"// before (cypress/e2e/spec.cy.js:132)\nconst usesIDSelectors = {\n  polymer: false\n}\n\n// after\nconst usesIDSelectors = {\n  polymer: false,\n  myframework: true   // app renders <input id=\"new-todo\">","handlingStrategy":"fallback","validationCode":"// Before adding a framework to the suite, snapshot which selectors its DOM exposes.\n// Run once in `cypress open --env framework=NEWFW` after commenting out the throw, then:\ncy.document().then(doc => {\n  const hasId = !!doc.querySelector('input#new-todo')\n  const hasClass = !!doc.querySelector('input.new-todo')\n  cy.log(`id=${hasId} class=${hasClass}`)\n  // If both true -> add to usesIDSelectors with the value you intend to drive the app with.\n})","typeGuard":null,"tryCatchPattern":"// Wrap the detection so an ambiguous framework is reported, not fatal, during onboarding.\ncy.document().then(doc => {\n  const fw = Cypress.env('framework')\n  if (!(fw in usesIDSelectors)) {\n    const hasId = findDeep(doc, 'input#new-todo')\n    const hasClass = findDeep(doc, 'input.new-todo')\n    if (hasId && hasClass) {\n      cy.log(`WARNING: ${fw} exposes both selector styles; defaulting to IDs. Add it to usesIDSelectors.`)\n      setSelectors(true); createTodoCommands(true); return\n    }\n  }\n  // ... existing branches\n})","preventionTips":["Register every supported framework in `usesIDSelectors` (line 132) rather than relying on runtime detection — detection is a fallback, not the primary path.","When porting a new TodoMVC example, decide its selector style up front and add the entry in the same PR.","Keep the map commented with the rendered markup shape so future contributors know why each value was chosen.","Treat the both-selectors-present throw as a CI signal: a new framework passing detection means the map needs an entry."],"tags":["cypress","selectors","runtime-detection","configuration","shadow-dom"],"backgroundTag":null,"analyzedSha":"ff43b02e59dfa604386bb382034b2cd07c2bcd8a","analyzedAt":"2026-08-13T10:00:20.798Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}