Skip to content

Commit 052892d

Browse files
sainthkhkuceb
andauthored
feat: support beforeinput event (#8411)
Co-authored-by: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
1 parent 5c01832 commit 052892d

File tree

3 files changed

+315
-23
lines changed

3 files changed

+315
-23
lines changed

packages/driver/cypress/integration/commands/actions/type_spec.js

Lines changed: 228 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const expectTextEndsWith = (expected) => {
2424
}
2525
}
2626

27+
const isChromium = Cypress.isBrowser({ family: 'chromium' })
28+
2729
describe('src/cy/commands/actions/type - #type', () => {
2830
beforeEach(() => {
2931
cy.visit('/fixtures/dom.html')
@@ -643,6 +645,7 @@ describe('src/cy/commands/actions/type - #type', () => {
643645
view: cy.state('window'),
644646
which: 65, // deprecated but fired by chrome
645647
})
648+
.not.have.property('inputType')
646649

647650
done()
648651
})
@@ -1810,6 +1813,219 @@ describe('src/cy/commands/actions/type - #type', () => {
18101813
})
18111814
})
18121815

1816+
// https://github.com/cypress-io/cypress/issues/7088
1817+
describe('beforeInput event', () => {
1818+
it('sends beforeinput in text input', () => {
1819+
const call1 = (e) => {
1820+
expect(e.code).not.exist
1821+
expect(e.data).eq(' ')
1822+
expect(e.inputType).eq('insertText')
1823+
stub.callsFake(call2)
1824+
}
1825+
const call2 = (e) => {
1826+
expect(e.code).not.exist
1827+
expect(e.data).eq('f')
1828+
expect(e.inputType).eq('insertText')
1829+
stub.callsFake(call3)
1830+
}
1831+
const call3 = (e) => {
1832+
expect(e.data).eq(null)
1833+
expect(e.inputType).eq('insertLineBreak')
1834+
stub.callsFake(call4)
1835+
}
1836+
const call4 = (e) => {
1837+
expect(e.data).eq(null)
1838+
expect(e.inputType).eq('deleteContentBackward')
1839+
stub.callsFake(call5)
1840+
}
1841+
const call5 = (e) => {
1842+
expect(e.data).eq(null)
1843+
expect(e.inputType).eq('deleteContentForward')
1844+
}
1845+
1846+
const stub = cy.stub()
1847+
.callsFake(call1)
1848+
1849+
cy.get('input:first')
1850+
.then(($el) => {
1851+
$el.val('foo bar baz')
1852+
$el[0].addEventListener('beforeinput', stub)
1853+
})
1854+
.type(' f\n{backspace}')
1855+
.type('{moveToStart}{del}')
1856+
.then(($el) => {
1857+
if (isChromium) {
1858+
expect(stub).callCount(5)
1859+
expect($el[0].value).eq('oo bar baz ')
1860+
} else {
1861+
expect(stub, 'should NOT send beforeinput unless in chromium based browser').not.called
1862+
}
1863+
})
1864+
})
1865+
1866+
it('sends beforeinput in textarea', () => {
1867+
const call1 = (e) => {
1868+
expect(e.code).not.exist
1869+
expect(e.data).eq(' ')
1870+
expect(e.inputType).eq('insertText')
1871+
stub.callsFake(call2)
1872+
}
1873+
const call2 = (e) => {
1874+
expect(e.code).not.exist
1875+
expect(e.data).eq('f')
1876+
expect(e.inputType).eq('insertText')
1877+
stub.callsFake(call3)
1878+
}
1879+
const call3 = (e) => {
1880+
expect(e.data).eq(null)
1881+
expect(e.inputType).eq('insertLineBreak')
1882+
stub.callsFake(call4)
1883+
}
1884+
const call4 = (e) => {
1885+
expect(e.data).eq(null)
1886+
expect(e.inputType).eq('deleteContentBackward')
1887+
stub.callsFake(call5)
1888+
}
1889+
const call5 = (e) => {
1890+
expect(e.data).eq(null)
1891+
expect(e.inputType).eq('deleteContentForward')
1892+
}
1893+
1894+
const stub = cy.stub()
1895+
.callsFake(call1)
1896+
1897+
cy.get('textarea:first')
1898+
.then(($el) => {
1899+
$el.val('foo bar baz')
1900+
$el[0].addEventListener('beforeinput', stub)
1901+
})
1902+
.type(' f\n{backspace}')
1903+
.type('{moveToStart}{del}')
1904+
.then(($el) => {
1905+
if (isChromium) {
1906+
expect(stub).callCount(5)
1907+
expect($el[0].value).eq('oo bar baz f')
1908+
} else {
1909+
expect(stub, 'should NOT send beforeinput unless in chromium based browser').not.called
1910+
}
1911+
})
1912+
})
1913+
1914+
it('sends beforeinput in [contenteditable]', () => {
1915+
const call1 = (e) => {
1916+
expect(e.code).not.exist
1917+
expect(e.data).eq(' ')
1918+
expect(e.inputType).eq('insertText')
1919+
stub.callsFake(call2)
1920+
}
1921+
const call2 = (e) => {
1922+
expect(e.code).not.exist
1923+
expect(e.data).eq('f')
1924+
expect(e.inputType).eq('insertText')
1925+
stub.callsFake(call3)
1926+
}
1927+
const call3 = (e) => {
1928+
expect(e.data).eq(null)
1929+
expect(e.inputType).eq('insertParagraph')
1930+
stub.callsFake(call4)
1931+
}
1932+
const call4 = (e) => {
1933+
expect(e.data).eq(null)
1934+
expect(e.inputType).eq('deleteContentBackward')
1935+
stub.callsFake(call5)
1936+
}
1937+
const call5 = (e) => {
1938+
expect(e.data).eq(null)
1939+
expect(e.inputType).eq('deleteContentForward')
1940+
}
1941+
1942+
const stub = cy.stub()
1943+
.callsFake(call1)
1944+
1945+
cy.get('#input-types [contenteditable]')
1946+
.then(($el) => {
1947+
$el.text('foo bar baz')
1948+
$el[0].addEventListener('beforeinput', stub)
1949+
})
1950+
.type(' f\n{backspace}')
1951+
.type('{moveToStart}{del}')
1952+
.then(($el) => {
1953+
if (isChromium) {
1954+
expect(stub).callCount(5)
1955+
expect($el[0].textContent).eq('oo bar baz f')
1956+
} else {
1957+
expect(stub, 'should NOT send beforeinput unless in chromium based browser').not.called
1958+
}
1959+
})
1960+
})
1961+
1962+
it('beforeinput special inputTypes', () => {
1963+
const call1 = (e) => {
1964+
expect(e.code).not.exist
1965+
expect(e.data).eq(null)
1966+
expect(e.inputType).eq('deleteWordForward')
1967+
stub.callsFake(call2)
1968+
}
1969+
const call2 = (e) => {
1970+
expect(e.code).not.exist
1971+
expect(e.data).eq(null)
1972+
expect(e.inputType).eq('deleteHardLineForward')
1973+
stub.callsFake(call3)
1974+
}
1975+
const call3 = (e) => {
1976+
expect(e.data).eq(null)
1977+
expect(e.inputType).eq('deleteWordBackward')
1978+
stub.callsFake(call4)
1979+
}
1980+
const call4 = (e) => {
1981+
expect(e.data).eq(null)
1982+
expect(e.inputType).eq('deleteHardLineBackward')
1983+
}
1984+
1985+
const stub = cy.stub()
1986+
.callsFake(call1)
1987+
1988+
cy.get('#input-types [contenteditable]')
1989+
.then(($el) => {
1990+
$el.text('foo bar baz')
1991+
$el[0].addEventListener('beforeinput', stub)
1992+
})
1993+
.type('{ctrl}{del}')
1994+
.type('{ctrl}{shift}{del}')
1995+
.type('{ctrl}{backspace}')
1996+
.type('{ctrl}{shift}{backspace}')
1997+
.then(($el) => {
1998+
if (isChromium) {
1999+
expect(stub).callCount(4)
2000+
} else {
2001+
expect(stub, 'should NOT send beforeinput unless in chromium based browser').not.called
2002+
}
2003+
})
2004+
})
2005+
2006+
it('can cancel beforeinput', () => {
2007+
let callCount = 0
2008+
2009+
cy.get('input:first')
2010+
.then(($el) => {
2011+
$el.val('foo bar baz')
2012+
$el[0].addEventListener('beforeinput', (e) => {
2013+
callCount++
2014+
e.preventDefault()
2015+
})
2016+
})
2017+
.type('foo')
2018+
.then(($el) => {
2019+
if (isChromium) {
2020+
expect(callCount).eq(3)
2021+
expect($el[0].value).eq('foo bar baz')
2022+
} else {
2023+
expect(callCount, 'should NOT send beforeinput unless in chromium based browser').eq(0)
2024+
}
2025+
})
2026+
})
2027+
})
2028+
18132029
// type follows focus
18142030
// https://github.com/cypress-io/cypress/issues/2240
18152031
describe('element reference loss', () => {
@@ -3013,18 +3229,20 @@ describe('src/cy/commands/actions/type - #type', () => {
30133229
// eslint-disable-next-line
30143230
console.table(table.data, table.columns)
30153231

3232+
const beforeInput = isChromium ? 'beforeinput, ' : ''
3233+
30163234
expect(table.name).to.eq('Keyboard Events')
30173235
const expectedTable = {
30183236
1: { 'Details': '{ code: MetaLeft, which: 91 }', Typed: '{cmd}', 'Events Fired': 'keydown', 'Active Modifiers': 'meta', 'Prevented Default': null, 'Target Element': $input[0] },
30193237
2: { 'Details': '{ code: AltLeft, which: 18 }', Typed: '{option}', 'Events Fired': 'keydown', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3020-
3: { 'Details': '{ code: KeyF, which: 70 }', Typed: 'f', 'Events Fired': 'keydown, keypress, textInput, input, keyup', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3021-
4: { 'Details': '{ code: KeyO, which: 79 }', Typed: 'o', 'Events Fired': 'keydown, keypress, textInput, input, keyup', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3022-
5: { 'Details': '{ code: KeyO, which: 79 }', Typed: 'o', 'Events Fired': 'keydown, keypress, textInput, input, keyup', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3023-
6: { 'Details': '{ code: Enter, which: 13 }', Typed: '{enter}', 'Events Fired': 'keydown, keypress, keyup', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3024-
7: { 'Details': '{ code: KeyB, which: 66 }', Typed: 'b', 'Events Fired': 'keydown, keypress, textInput, input, keyup', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3238+
3: { 'Details': '{ code: KeyF, which: 70 }', Typed: 'f', 'Events Fired': `keydown, keypress, ${beforeInput}textInput, input, keyup`, 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3239+
4: { 'Details': '{ code: KeyO, which: 79 }', Typed: 'o', 'Events Fired': `keydown, keypress, ${beforeInput}textInput, input, keyup`, 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3240+
5: { 'Details': '{ code: KeyO, which: 79 }', Typed: 'o', 'Events Fired': `keydown, keypress, ${beforeInput}textInput, input, keyup`, 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3241+
6: { 'Details': '{ code: Enter, which: 13 }', Typed: '{enter}', 'Events Fired': `keydown, keypress, ${beforeInput}keyup`, 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3242+
7: { 'Details': '{ code: KeyB, which: 66 }', Typed: 'b', 'Events Fired': `keydown, keypress, ${beforeInput}textInput, input, keyup`, 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
30253243
8: { 'Details': '{ code: ArrowLeft, which: 37 }', Typed: '{leftarrow}', 'Events Fired': 'keydown, keyup', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3026-
9: { 'Details': '{ code: Delete, which: 46 }', Typed: '{del}', 'Events Fired': 'keydown, input, keyup', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3027-
10: { 'Details': '{ code: Enter, which: 13 }', Typed: '{enter}', 'Events Fired': 'keydown, keypress, keyup', 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3244+
9: { 'Details': '{ code: Delete, which: 46 }', Typed: '{del}', 'Events Fired': `keydown, ${beforeInput}input, keyup`, 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
3245+
10: { 'Details': '{ code: Enter, which: 13 }', Typed: '{enter}', 'Events Fired': `keydown, keypress, ${beforeInput}keyup`, 'Active Modifiers': 'alt, meta', 'Prevented Default': null, 'Target Element': $input[0] },
30283246
11: { 'Details': '{ code: MetaLeft, which: 91 }', Typed: '{cmd}', 'Events Fired': 'keyup', 'Active Modifiers': 'alt', 'Prevented Default': null, 'Target Element': $input[0] },
30293247
12: { 'Details': '{ code: AltLeft, which: 18 }', Typed: '{option}', 'Events Fired': 'keyup', 'Active Modifiers': null, 'Prevented Default': null, 'Target Element': $input[0] },
30303248
}
@@ -3040,8 +3258,10 @@ describe('src/cy/commands/actions/type - #type', () => {
30403258
cy.get(':text:first').type('f').then(function ($el) {
30413259
const table = this.lastLog.invoke('consoleProps').table[2]()
30423260

3261+
const beforeInput = isChromium ? 'beforeinput, ' : ''
3262+
30433263
expect(table.data).to.deep.eq({
3044-
1: { Typed: 'f', 'Events Fired': 'keydown, keypress, textInput, input, keyup', 'Active Modifiers': null, Details: '{ code: KeyF, which: 70 }', 'Prevented Default': null, 'Target Element': $el[0] },
3264+
1: { Typed: 'f', 'Events Fired': `keydown, keypress, ${beforeInput}textInput, input, keyup`, 'Active Modifiers': null, Details: '{ code: KeyF, which: 70 }', 'Prevented Default': null, 'Target Element': $el[0] },
30453265
})
30463266
})
30473267
})

0 commit comments

Comments
 (0)