Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/direct-addvalue-gets-an-action-row.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@wdio/devtools-service": patch
---

Give a direct `addValue` its own action row. It was excluded from the trace action vocabulary on the grounds that WDIO fires it inside `setValue`, where mapping it would double-count — but that assumed it only ever appears nested. A direct `addValue`, which is idiomatic on Appium, produced no action at all: the typing step was simply missing from the trace, and a spec doing click → `addValue` → `getText` exported two rows instead of three.

The double-count the exclusion guarded against cannot happen. The service logs a command only when it matches the top of its own command stack, and that stack is pushed for top-level user commands alone — so the nested `addValue` never reaches the command log to be mapped. Neither the Selenium nor the Nightwatch adapter emits a command by that name at all; `addValue` is WDIO's, and Selenium's equivalent `sendKeys` appends too and has always mapped to a fill. It renders the same way.

`clearValue` stays excluded: it has the same nesting story but no direct use that currently goes unrecorded.
29 changes: 29 additions & 0 deletions packages/service/tests/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,4 +442,33 @@ describe('DevtoolsService - Screencast Integration', () => {
expect(ScreencastRecorder).toHaveBeenCalled()
expect(mockScreencastRecorder.start).toHaveBeenCalledWith(mockBrowser)
})

// #352 mapped `addValue` into ACTION_MAP. It was excluded because WDIO fires it
// INSIDE setValue, and mapping it was assumed to double-count — this is the
// guarantee that makes it safe: only a command matching the top of the stack is
// logged, so the nested call never reaches the command log to be mapped at all.
describe('a command nested inside another is not logged', () => {
it('logs setValue once, not setValue plus its inner addValue', async () => {
service.beforeCommand('setValue' as any, ['hello'])
// WDIO issues this from within setValue, below the top-level boundary.
service.beforeCommand('addValue' as any, ['hello'])
service.afterCommand('addValue' as any, ['hello'], undefined)
await service.afterCommand('setValue' as any, ['hello'], undefined)

const logged = mockSessionCapturerInstance.afterCommand.mock.calls.map(
(call: unknown[]) => call[1]
)
expect(logged).toEqual(['setValue'])
})

it('logs a direct addValue, which is the row #352 was missing', async () => {
service.beforeCommand('addValue' as any, ['hello'])
await service.afterCommand('addValue' as any, ['hello'], undefined)

const logged = mockSessionCapturerInstance.afterCommand.mock.calls.map(
(call: unknown[]) => call[1]
)
expect(logged).toEqual(['addValue'])
})
})
})
6 changes: 5 additions & 1 deletion packages/shared/src/action-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ export type { TraceAction }
export { ASSERT_ACTION_CLASS, mapAssertCommand }

// Excluded by design:
// clearValue / addValue — WDIO fires these inside setValue (duplicate events).
// clearValue — WDIO fires it inside setValue (duplicate events). `addValue`
// was excluded for the same reason and is now mapped: the nested call never
// reaches the command log (the service logs only a command matching the top
// of its stack), and a DIRECT addValue — idiomatic on Appium — was
// producing no action row at all.
// executeScript — Selenium's `until` polling fires it ~50ms; also recurses
// because @wdio/elements uses executeScript inside captureActionSnapshot.
// WDIO's user-facing `execute`/`executeAsync` are still captured.
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/trace-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const ACTION_MAP: Record<string, TraceAction> = {
click: { class: 'Element', method: 'click' },
doubleClick: { class: 'Element', method: 'dblclick' },
setValue: { class: 'Element', method: 'fill' },
addValue: { class: 'Element', method: 'fill' },
selectByVisibleText: { class: 'Element', method: 'selectOption' },
moveTo: { class: 'Element', method: 'hover' },
scrollIntoView: { class: 'Element', method: 'scrollIntoViewIfNeeded' },
Expand Down
26 changes: 25 additions & 1 deletion packages/shared/tests/action-mapping.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ describe('mapCommandToAction for read/query commands', () => {
describe('mapCommandToAction still excludes noisy/internal commands', () => {
it.each([
'clearValue',
'addValue',
'executeScript',
'$',
'$$',
Expand Down Expand Up @@ -241,3 +240,28 @@ describe('assertion command names', () => {
expect(mapCommandToAction('expect')).toBeNull()
})
})

// A direct addValue is idiomatic on Appium, and it was excluded from the map on
// the assumption it only ever appears nested inside setValue. The typing step
// was then missing from the trace entirely — a click → addValue → getText spec
// produced two action rows.
describe('mapCommandToAction for addValue (#352)', () => {
it('maps it to a fill, like the other append-style typing commands', () => {
expect(mapCommandToAction('addValue')).toEqual({
class: 'Element',
method: 'fill'
})
})

it('renders the same way selenium sendKeys does', () => {
expect(mapCommandToAction('addValue')).toEqual(
mapCommandToAction('sendKeys')
)
})

// Still excluded: unlike addValue it has no direct-use case that goes
// unrecorded, and WDIO fires it inside setValue.
it('leaves clearValue unmapped', () => {
expect(mapCommandToAction('clearValue')).toBeNull()
})
})
Loading