{"record":{"id":"92ea96b790058523","repo":"grafana/k6","slug":"tapping-on-q-w","errorCode":null,"errorMessage":"tapping on %q: %w","messagePattern":"tapping on %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame.go","lineNumber":1801,"sourceCode":"// SetInputFiles sets input files for the selected element.\nfunc (f *Frame) SetInputFiles(selector string, pfiles *Files, popts *FrameSetInputFilesOptions) error {\n\tf.log.Debugf(\"Frame:SetInputFiles\", \"fid:%s furl:%q sel:%q\", f.ID(), f.URL(), selector)\n\n\tif err := f.setInputFiles(selector, pfiles, popts); err != nil {\n\t\treturn fmt.Errorf(\"setting input files on %q: %w\", selector, err)\n\t}\n\n\tapplySlowMo(f.ctx)\n\n\treturn nil\n}\n\n// Tap the first element that matches the selector.\nfunc (f *Frame) Tap(selector string, opts *FrameTapOptions) error {\n\tf.log.Debugf(\"Frame:Tap\", \"fid:%s furl:%q sel:%q\", f.ID(), f.URL(), selector)\n\n\tif err := f.tap(selector, opts); err != nil {\n\t\treturn fmt.Errorf(\"tapping on %q: %w\", selector, err)\n\t}\n\n\tapplySlowMo(f.ctx)\n\n\treturn nil\n}\n\nfunc (f *Frame) tap(selector string, opts *FrameTapOptions) error {\n\ttap := func(apiCtx context.Context, handle *ElementHandle, p *Position) (any, error) {\n\t\treturn nil, handle.tap(apiCtx, p)\n\t}\n\tact := f.newPointerAction(\n\t\tselector, DOMElementStateAttached, opts.Strict, tap, &opts.ElementHandleBasePointerOptions,\n\t)\n\tif _, err := call(f.ctx, act, opts.Timeout); err != nil {\n\t\treturn errorFromDOMError(err)\n\t}\n","sourceCodeStart":1783,"sourceCodeEnd":1819,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame.go#L1783-L1819","documentation":"Frame.Tap() wraps the internal pointer action as 'tapping on \"<selector>\": <cause>'. The cause is the real signal: wait-for-element timeout, actionability failure (not visible, not stable, hit-target check fails because something else covers the point), or context cancellation.","triggerScenarios":"frame.tap(sel) with no touch emulation metadata is fine, but fails when the element is off-viewport or covered; selector matches multiple elements with strict:true; element detaches between resolution and the tap; page/frame closed.","commonSituations":"Mobile-emulation tests tapping buttons under sticky headers/cookie banners; elements that animate (hit-target check sees them moving); tapping elements that only appear after async load without waiting.","solutions":["Wait for visibility and stability: await frame.waitForSelector(sel, { state: 'visible' }) and let animations finish before tapping.","Scroll the element into view (locator.scrollIntoViewIfNeeded or evaluate scrollIntoView) first.","Pass { force: true } to bypass hit-target/actionability checks when the overlay is intentional.","Use a unique selector or { strict: false } to avoid ambiguity."],"exampleFix":"// before\nawait frame.tap('#checkout'); // sticky header covers it\n\n// after\nawait frame.waitForSelector('#checkout', { state: 'visible' });\nawait frame.tap('#checkout', { force: true });","handlingStrategy":"validation","validationCode":"await frame.waitForSelector(sel, { state: 'visible' });\nawait frame.evaluate(s => document.querySelector(s)?.scrollIntoView({ block: 'center' }), sel);","typeGuard":null,"tryCatchPattern":"try { await frame.tap(sel); }\ncatch (e) { if (/tapping on/.test(e.message) && /timeout|not visible|hit target/i.test(e.message)) { /* retry after scroll or force */ } throw e; }","preventionTips":["Scroll targets into view and let animations settle before tapping","Use unique selectors with strict mode","Reserve force:true for verified overlays"],"tags":["browser","touch","actionability","hit-target","timeout"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}