Add Cypress.dom.* to TS type declarations (#5298)

* add isDetached to type declaration

* add rest of dom function declarations

* remove any type union

* remove duplicate declaration

* add docstrings for documented types

* .js => .ts

* update types + tests

fix ts lint

* add Cypress.dom type tests

Co-authored-by: Zach Bloomquist <github@chary.us>
Co-authored-by: Jennifer Shehane <shehane.jennifer@gmail.com>
This commit is contained in:
Kyle Roberts
2020-03-02 14:22:13 -05:00
committed by GitHub
parent de5e672364
commit 855657d334
6 changed files with 220 additions and 18 deletions

108
cli/types/index.d.ts vendored
View File

@@ -122,6 +122,36 @@ declare namespace Cypress {
clear: (keys?: string[]) => void
}
interface ViewportPosition extends WindowPosition {
right: number
bottom: number
}
interface WindowPosition {
top: number
left: number
topCenter: number
leftCenter: number
}
interface ElementPositioning {
scrollTop: number
scrollLeft: number
width: number
height: number
fromElViewport: ViewportPosition
fromElWindow: WindowPosition
fromAutWindow: WindowPosition
}
interface ElementCoordinates {
width: number
height: number
fromElViewport: ViewportPosition & { x: number, y: number }
fromElWindow: WindowPosition & { x: number, y: number }
fromAutWindow: WindowPosition & { x: number, y: number }
}
/**
* Several libraries are bundled with Cypress by default.
*
@@ -371,7 +401,85 @@ declare namespace Cypress {
* @see https://on.cypress.io/dom
*/
dom: {
/**
* Returns a jQuery object obtained by wrapping an object in jQuery.
*/
wrap(wrappingElement_function: JQuery.Selector | JQuery.htmlString | Element | JQuery | ((index: number) => string | JQuery)): JQuery
query(selector: JQuery.Selector, context?: Element | JQuery): JQuery
/**
* Returns an array of raw elements pulled out from a jQuery object.
*/
unwrap(obj: any): any
/**
* Returns a boolean indicating whether an object is a DOM object.
*/
isDom(obj: any): boolean
isType(element: JQuery | HTMLElement , type: string): boolean
/**
* Returns a boolean indicating whether an element is visible.
*/
isVisible(element: JQuery | HTMLElement): boolean
/**
* Returns a boolean indicating whether an element is hidden.
*/
isHidden(element: JQuery | HTMLElement): boolean
/**
* Returns a boolean indicating whether an element can receive focus.
*/
isFocusable(element: JQuery | HTMLElement): boolean
isTextLike(element: JQuery | HTMLElement): boolean
/**
* Returns a boolean indicating whether an element is scrollable.
*/
isScrollable(element: Window | JQuery | HTMLElement): boolean
/**
* Returns a boolean indicating whether an element currently has focus.
*/
isFocused(element: JQuery | HTMLElement): boolean
/**
* Returns a boolean indicating whether an element is detached from the DOM.
*/
isDetached(element: JQuery | HTMLElement): boolean
/**
* Returns a boolean indicating whether an element is attached to the DOM.
*/
isAttached(element: JQuery | HTMLElement | Window | Document): boolean
isSelector(element: JQuery | HTMLElement, selector: JQuery.Selector): boolean
/**
* Returns a boolean indicating whether an element is a descendent of another element.
*/
isDescendent(element1: JQuery | HTMLElement, element2: JQuery | HTMLElement): boolean
/**
* Returns a boolean indicating whether an object is a DOM element.
*/
isElement(obj: any): boolean
/**
* Returns a boolean indicating whether a node is of document type.
*/
isDocument(obj: any): boolean
/**
* Returns a boolean indicating whether an object is a window object.
*/
isWindow(obj: any): boolean
/**
* Returns a boolean indicating whether an object is a jQuery object.
*/
isJquery(obj: any): boolean
isInputType(element: JQuery | HTMLElement, type: string | string[]): boolean
stringify(element: JQuery | HTMLElement, form: string): string
getElements(element: JQuery): JQuery | HTMLElement[]
getContainsSelector(text: string, filter?: string): JQuery.Selector
getFirstDeepestElement(elements: HTMLElement[], index?: number): HTMLElement
getWindowByElement(element: JQuery | HTMLElement): JQuery | HTMLElement
getReasonIsHidden(element: JQuery | HTMLElement): string
getFirstScrollableParent(element: JQuery | HTMLElement): JQuery | HTMLElement
getFirstFixedOrStickyPositionParent(element: JQuery | HTMLElement): JQuery | HTMLElement
getFirstStickyPositionParent(element: JQuery | HTMLElement): JQuery | HTMLElement
getCoordsByPosition(left: number, top: number, xPosition?: string, yPosition?: string): number
getElementPositioning(element: JQuery | HTMLElement): ElementPositioning
getElementAtPointFromViewport(doc: Document, x: number, y: number): Element | null
getElementCoordinatesByPosition(element: JQuery | HTMLElement, position: string): ElementCoordinates
getElementCoordinatesByPositionRelativeToXY(element: JQuery | HTMLElement, x: number, y: number): ElementPositioning
}
/**

View File

@@ -355,3 +355,80 @@ namespace CypressBrowserTests {
Cypress.isBrowser({family: 'foo'}) // $ExpectError
Cypress.isBrowser() // $ExpectError
}
namespace CypressDomTests {
const obj: any = {}
const el = {} as any as HTMLElement
const jel = {} as any as JQuery
const doc = {} as any as Document
Cypress.dom.wrap((x: number) => 'a') // $ExpectType JQuery<HTMLElement>
Cypress.dom.query('foo', el) // $ExpectType JQuery<HTMLElement>
Cypress.dom.unwrap(obj) // $ExpectType any
Cypress.dom.isDom(obj) // $ExpectType boolean
Cypress.dom.isType(el, 'foo') // $ExpectType boolean
Cypress.dom.isVisible(el) // $ExpectType boolean
Cypress.dom.isHidden(el) // $ExpectType boolean
Cypress.dom.isFocusable(el) // $ExpectType boolean
Cypress.dom.isTextLike(el) // $ExpectType boolean
Cypress.dom.isScrollable(el) // $ExpectType boolean
Cypress.dom.isFocused(el) // $ExpectType boolean
Cypress.dom.isDetached(el) // $ExpectType boolean
Cypress.dom.isAttached(el) // $ExpectType boolean
Cypress.dom.isSelector(el, 'foo') // $ExpectType boolean
Cypress.dom.isDescendent(el, el) // $ExpectType boolean
Cypress.dom.isElement(obj) // $ExpectType boolean
Cypress.dom.isDocument(obj) // $ExpectType boolean
Cypress.dom.isWindow(obj) // $ExpectType boolean
Cypress.dom.isJquery(obj) // $ExpectType boolean
Cypress.dom.isInputType(el, 'number') // $ExpectType boolean
Cypress.dom.stringify(el, 'foo') // $ExpectType string
Cypress.dom.getElements(jel) // $ExpectType JQuery<HTMLElement> | HTMLElement[]
Cypress.dom.getContainsSelector('foo', 'bar') // $ExpectType string
Cypress.dom.getFirstDeepestElement([el], 1) // $ExpectType HTMLElement
Cypress.dom.getWindowByElement(el) // $ExpectType HTMLElement | JQuery<HTMLElement>
Cypress.dom.getReasonIsHidden(el) // $ExpectType string
Cypress.dom.getFirstScrollableParent(el) // $ExpectType HTMLElement | JQuery<HTMLElement>
Cypress.dom.getFirstFixedOrStickyPositionParent(el) // $ExpectType HTMLElement | JQuery<HTMLElement>
Cypress.dom.getFirstStickyPositionParent(el) // $ExpectType HTMLElement | JQuery<HTMLElement>
Cypress.dom.getCoordsByPosition(1, 2) // $ExpectType number
Cypress.dom.getElementPositioning(el) // $ExpectType ElementPositioning
Cypress.dom.getElementAtPointFromViewport(doc, 1, 2) // $ExpectType Element | null
Cypress.dom.getElementCoordinatesByPosition(el, 'top') // $ExpectType ElementCoordinates
Cypress.dom.getElementCoordinatesByPositionRelativeToXY(el, 1, 2) // $ExpectType ElementPositioning
Cypress.dom.wrap() // $ExpectError
Cypress.dom.query(el, 'foo') // $ExpectError
Cypress.dom.unwrap() // $ExpectError
Cypress.dom.isDom() // $ExpectError
Cypress.dom.isType(el) // $ExpectError
Cypress.dom.isVisible('') // $ExpectError
Cypress.dom.isHidden('') // $ExpectError
Cypress.dom.isFocusable('') // $ExpectError
Cypress.dom.isTextLike('') // $ExpectError
Cypress.dom.isScrollable('') // $ExpectError
Cypress.dom.isFocused('') // $ExpectError
Cypress.dom.isDetached('') // $ExpectError
Cypress.dom.isAttached('') // $ExpectError
Cypress.dom.isSelector('', 'foo') // $ExpectError
Cypress.dom.isDescendent('', '') // $ExpectError
Cypress.dom.isElement() // $ExpectError
Cypress.dom.isDocument() // $ExpectError
Cypress.dom.isWindow() // $ExpectError
Cypress.dom.isJquery() // $ExpectError
Cypress.dom.isInputType('', 'number') // $ExpectError
Cypress.dom.stringify('', 'foo') // $ExpectError
Cypress.dom.getElements(el) // $ExpectError
Cypress.dom.getContainsSelector(el, 'bar') // $ExpectError
Cypress.dom.getFirstDeepestElement(el, 1) // $ExpectError
Cypress.dom.getWindowByElement('') // $ExpectError
Cypress.dom.getReasonIsHidden('') // $ExpectError
Cypress.dom.getFirstScrollableParent('') // $ExpectError
Cypress.dom.getFirstFixedOrStickyPositionParent('') // $ExpectError
Cypress.dom.getFirstStickyPositionParent('') // $ExpectError
Cypress.dom.getCoordsByPosition(1) // $ExpectError
Cypress.dom.getElementPositioning('') // $ExpectError
Cypress.dom.getElementAtPointFromViewport(el, 1, 2) // $ExpectError
Cypress.dom.getElementCoordinatesByPosition(doc, 'top') // $ExpectError
Cypress.dom.getElementCoordinatesByPositionRelativeToXY(doc, 1, 2) // $ExpectError
}

View File

@@ -1,6 +1,13 @@
const $ = Cypress.$.bind(Cypress)
declare namespace Cypress {
interface cy {
state(key: 'document'): Document
}
}
describe('src/dom/coordinates', () => {
const $ = Cypress.$.bind(Cypress)
let doc: Document
before(() => {
return cy
.visit('/fixtures/generic.html')
@@ -10,9 +17,9 @@ describe('src/dom/coordinates', () => {
})
beforeEach(function () {
this.doc = cy.state('document')
doc = cy.state('document')
$(this.doc.body).empty().html(this.body)
$(doc.body).empty().html(this.body)
this.$button = $('<button style=\'position: absolute; top: 25px; left: 50px; width: 100px; line-height: 50px; padding: 10px; margin: 10px; border: 10px solid black\'>foo</button>')
.appendTo(cy.$$('body'))
@@ -24,8 +31,8 @@ describe('src/dom/coordinates', () => {
it('returns the leftCenter and topCenter normalized', function () {
const win = Cypress.dom.getWindowByElement(this.$button.get(0))
const scrollY = Object.getOwnPropertyDescriptor(win, 'scrollY')
const scrollX = Object.getOwnPropertyDescriptor(win, 'scrollX')
const scrollY = Object.getOwnPropertyDescriptor(win, 'scrollY')!
const scrollX = Object.getOwnPropertyDescriptor(win, 'scrollX')!
Object.defineProperty(win, 'scrollY', {
value: 10,
@@ -63,17 +70,17 @@ describe('src/dom/coordinates', () => {
context('.getElementAtPointFromViewport', () => {
it('returns same element based on x/y coords', function () {
expect(Cypress.dom.getElementAtPointFromViewport(this.doc, 100, 60)).to.eq(this.$button.get(0))
expect(Cypress.dom.getElementAtPointFromViewport(doc, 100, 60)).to.eq(this.$button.get(0))
})
it('does not return if element is hidden', function () {
this.$button.hide()
expect(Cypress.dom.getElementAtPointFromViewport(this.doc, 100, 60)).not.to.eq(this.$button.get(0))
expect(Cypress.dom.getElementAtPointFromViewport(doc, 100, 60)).not.to.eq(this.$button.get(0))
})
it('returns null if no element was found', function () {
expect(Cypress.dom.getElementAtPointFromViewport(this.doc, 1e9, 1e9)).to.be.null
expect(Cypress.dom.getElementAtPointFromViewport(doc, 1e9, 1e9)).to.be.null
})
})

View File

@@ -1,13 +1,19 @@
const $ = Cypress.$.bind(Cypress)
declare namespace Cypress {
interface cy {
state(key: 'window'): Window
}
}
describe('src/dom/elements', () => {
const $ = Cypress.$.bind(Cypress)
context('.isAttached', () => {
beforeEach(() => {
cy.visit('/fixtures/iframe-outer.html')
})
it('no elements', () => {
const $el = $(null)
const $el = $(null!)
expect(Cypress.dom.isAttached($el)).to.be.false
})
@@ -68,9 +74,9 @@ describe('src/dom/elements', () => {
it('element in iframe', (done) => {
cy.get('iframe').then(($iframe) => {
const $doc = $iframe.contents()
const $doc = $iframe.contents() as JQuery<Document>
const $btn = $doc.find('button')
const $btn = $doc.find('button') as unknown as JQuery<HTMLButtonElement>
expect($btn.length).to.eq(1)
@@ -84,7 +90,7 @@ describe('src/dom/elements', () => {
done()
})
const win = $doc.get(0).defaultView
const win = $doc.get(0).defaultView!
win.location.reload()
})
@@ -93,7 +99,7 @@ describe('src/dom/elements', () => {
context('.isDetached', () => {
it('opposite of attached', () => {
const $el = $(null)
const $el = $(null!)
expect(Cypress.dom.isDetached($el)).to.be.true
})

View File

@@ -1,4 +1,6 @@
const { $ } = Cypress
declare interface Window {
jquery: Function
}
describe('src/dom/jquery', () => {
context('.isJquery', () => {
@@ -9,7 +11,7 @@ describe('src/dom/jquery', () => {
})
it('is true for actual jquery instances', () => {
expect(Cypress.dom.isJquery($(':first'))).to.be.true
expect(Cypress.dom.isJquery(Cypress.$(':first'))).to.be.true
})
})
})

View File

@@ -1,7 +1,8 @@
const $dom = Cypress.dom
const $ = Cypress.$.bind(Cypress)
describe('src/cypress/dom/visibility', () => {
const $ = Cypress.$.bind(Cypress)
beforeEach(() => {
cy.visit('/fixtures/generic.html')
})
@@ -13,7 +14,7 @@ describe('src/cypress/dom/visibility', () => {
it('throws when not passed a DOM element', () => {
const fn = () => {
$dom.isHidden(null)
$dom.isHidden(null!)
}
expect(fn).to.throw('Cypress.dom.isHidden() failed because it requires a DOM element. The subject received was: \'null\'')
@@ -27,6 +28,7 @@ describe('src/cypress/dom/visibility', () => {
it('throws when not passed a DOM element', () => {
const fn = () => {
// @ts-ignore
$dom.isVisible('form')
}