use typescript in driver (#5383)

* upgrade eslint-plugin-dev to 5.0.0

* more [lint fixes]

* use typescript in driver

* cleanup minor

* remove unneeded file

* mild refactor webpack configs

* fix elements.ts

* fix isTextLike signature change

* fix isType check, fix type_spec

* actually use webpack-preprocessor

* fix isType domUtil

* fix connectors_spec

* cleanup connectors_spec

* address feedback- cleanup, add comments, refac

* answer: no, need index.js
This commit is contained in:
Ben Kucera
2019-10-18 12:46:46 -04:00
committed by Brian Mann
parent c1416035e9
commit 033689f8ba
22 changed files with 301 additions and 185 deletions

View File

@@ -16,6 +16,7 @@
"@cypress/sinon-chai": "1.1.0",
"@cypress/underscore.inflection": "1.0.1",
"@cypress/unique-selector": "0.4.2",
"@cypress/webpack-preprocessor": "4.1.0",
"@cypress/what-is-circular": "1.0.1",
"angular": "1.7.7",
"backbone": "1.4.0",
@@ -60,6 +61,7 @@
"url-parse": "1.4.7",
"vanilla-text-mask": "5.1.1",
"wait-on": "3.3.0",
"webpack": "4.41.0",
"zone.js": "0.9.0"
},
"files": [

43
packages/driver/src/config/lodash.d.ts vendored Normal file
View File

@@ -0,0 +1,43 @@
// const _ = require('lodash')
import _ from 'lodash'
import underscoreInflection from '@cypress/underscore.inflection'
import clean from 'underscore.string/clean'
import count from 'underscore.string/count'
import isBlank from 'underscore.string/isBlank'
import toBoolean from 'underscore.string/toBoolean'
import capitalize from 'underscore.string/capitalize'
const inflection = underscoreInflection(_)
// only export exactly what we need, nothing more!
_.mixin({
clean,
count,
isBlank,
toBoolean,
capitalize, // its mo' better the lodash version
ordinalize: inflection.ordinalize,
})
declare module 'lodash' {
export interface LoDashExplicitWrapper<TValue> {
clean(...args): LoDashExplicitWrapper<TValue>
count(...args): LoDashExplicitWrapper<TValue>
isBlank(...args): LoDashExplicitWrapper<TValue>
toBoolean(...args): LoDashExplicitWrapper<TValue>
capitalize(...args): LoDashExplicitWrapper<TValue>
ordinalize(...args): LoDashExplicitWrapper<TValue>
}
export interface LodashStatic<TValue> {
clean(...args): LoDashExplicitWrapper<TValue>
count(...args): LoDashExplicitWrapper<TValue>
isBlank(...args): LoDashExplicitWrapper<TValue>
toBoolean(...args): LoDashExplicitWrapper<TValue>
capitalize(...args): LoDashExplicitWrapper<TValue>
ordinalize(...args): LoDashExplicitWrapper<TValue>
}
}
export default _

View File

@@ -11,3 +11,5 @@ _.mixin({
capitalize: require('underscore.string/capitalize'), // its mo' better the lodash version
ordinalize: inflection.ordinalize,
})
export default _

View File

@@ -111,7 +111,7 @@ module.exports = function (Commands, Cypress, cy, state, config) {
}
const isBody = options.$el.is('body')
const isTextLike = $dom.isTextLike(options.$el)
const isTextLike = $dom.isTextLike(options.$el.get(0))
const isDate = $dom.isType(options.$el, 'date')
const isTime = $dom.isType(options.$el, 'time')
const isMonth = $dom.isType(options.$el, 'month')
@@ -537,7 +537,7 @@ module.exports = function (Commands, Cypress, cy, state, config) {
const node = $dom.stringify($el)
if (!$dom.isTextLike($el)) {
if (!$dom.isTextLike($el.get(0))) {
const word = $utils.plural(subject, 'contains', 'is')
$utils.throwErrByPath('clear.invalid_element', {

View File

@@ -117,7 +117,7 @@ create = (state, expect) ->
# readonly can only be applied to input/textarea
# not on checkboxes, radios, etc..
if $dom.isTextLike(subject) and subject.prop("readonly")
if $dom.isTextLike(subject.get(0)) and subject.prop("readonly")
node = $dom.stringify(subject)
$utils.throwErrByPath("dom.readonly", {

View File

@@ -359,8 +359,8 @@ const create = (state) => {
'{shift}': 'shift',
},
type (options = {}) {
_.defaults(options, {
type (opts = {}) {
const options = _.defaults(opts, {
delay: 10,
parseSpecialCharSequences: true,
onEvent () {},
@@ -599,7 +599,7 @@ const create = (state) => {
})
},
ensureKey (el, key, options, fn) {
ensureKey (el, key, options, fn?: Function) {
_.defaults(options, {
prevText: null,
})
@@ -697,7 +697,7 @@ const create = (state) => {
resetModifiers (doc) {
const activeEl = $elements.getActiveElByDocument(doc)
const activeModifiers = kb.getActiveModifiers(state)
const activeModifiers = kb.getActiveModifiers()
for (let modifier in activeModifiers) {
const isActivated = activeModifiers[modifier]
@@ -718,7 +718,7 @@ const create = (state) => {
return kb
}
module.exports = {
export {
create,
toModifiersEventOptions,
modifiersToString,

View File

@@ -1,17 +1,18 @@
const _ = require('lodash')
const $ = require('jquery')
const $jquery = require('./jquery')
const $window = require('./window')
const $document = require('./document')
const $utils = require('../cypress/utils')
const $selection = require('./selection')
const { parentHasDisplayNone } = require('./visibility')
// NOT patched jquery
import $ from 'jquery'
import * as $jquery from './jquery'
import * as $window from './window'
import * as $document from './document'
import $utils from '../cypress/utils.coffee'
import * as $selection from './selection'
import _ from '../config/lodash'
import { parentHasDisplayNone } from './visibility'
const { wrap } = $jquery
const fixedOrStickyRe = /(fixed|sticky)/
const focusable = [
const focusableSelectors = [
'a[href]',
'area[href]',
'input:not([disabled])',
@@ -20,17 +21,43 @@ const focusable = [
'button:not([disabled])',
'iframe',
'[tabindex]',
'[contentEditable]',
'[contenteditable]',
]
const inputTypeNeedSingleValueChangeRe = /^(date|time|month|week)$/
const canSetSelectionRangeElementRe = /^(text|search|URL|tel|password)$/
declare global {
interface Window {
Element: typeof Element
HTMLElement: typeof HTMLElement
HTMLInputElement: typeof HTMLInputElement
HTMLSelectElement: typeof HTMLSelectElement
HTMLButtonElement: typeof HTMLButtonElement
HTMLOptionElement: typeof HTMLOptionElement
HTMLTextAreaElement: typeof HTMLTextAreaElement
Selection: typeof Selection
SVGElement: typeof SVGElement
EventTarget: typeof EventTarget
Document: typeof Document
}
interface Selection {
modify: Function
}
}
// rules for native methods and props
// if a setter or getter or function then add a native method
// if a traversal, don't
const descriptor = (klass, prop) => {
return Object.getOwnPropertyDescriptor(window[klass].prototype, prop)
const descriptor = <T extends keyof Window, K extends keyof Window[T]['prototype']>(klass: T, prop: K) => {
const descriptor = Object.getOwnPropertyDescriptor(window[klass].prototype, prop)
if (descriptor === undefined) {
throw new Error(`Error, could not get property descriptor for ${klass} ${prop}. This should never happen`)
}
return descriptor
}
const _getValue = function () {
@@ -83,6 +110,8 @@ const _getSelectionStart = function () {
if (isTextarea(this)) {
return descriptor('HTMLTextAreaElement', 'selectionStart').get
}
throw new Error('this should never happen, cannot get selectionStart')
}
const _getSelectionEnd = function () {
@@ -93,6 +122,8 @@ const _getSelectionEnd = function () {
if (isTextarea(this)) {
return descriptor('HTMLTextAreaElement', 'selectionEnd').get
}
throw new Error('this should never happen, cannot get selectionEnd')
}
const _nativeFocus = function () {
@@ -153,6 +184,8 @@ const _setType = function () {
if (isButton(this)) {
return descriptor('HTMLButtonElement', 'type').set
}
throw new Error('this should never happen, cannot set type')
}
const _getType = function () {
@@ -163,6 +196,8 @@ const _getType = function () {
if (isButton(this)) {
return descriptor('HTMLButtonElement', 'type').get
}
throw new Error('this should never happen, cannot get type')
}
const nativeGetters = {
@@ -174,7 +209,7 @@ const nativeGetters = {
type: _getType,
activeElement: descriptor('Document', 'activeElement').get,
body: descriptor('Document', 'body').get,
frameElement: Object.getOwnPropertyDescriptor(window, 'frameElement').get,
frameElement: Object.getOwnPropertyDescriptor(window, 'frameElement')!.get,
}
const nativeSetters = {
@@ -199,9 +234,9 @@ const nativeMethods = {
select: _nativeSelect,
}
const tryCallNativeMethod = (...args) => {
const tryCallNativeMethod = (obj, fn, ...args) => {
try {
return callNativeMethod(...args)
return callNativeMethod(obj, fn, ...args)
} catch (err) {
return
}
@@ -224,9 +259,8 @@ const callNativeMethod = function (obj, fn, ...args) {
return retFn
}
const getNativeProp = function (obj, prop) {
const nativeProp = nativeGetters[prop]
const getNativeProp = function<T, K extends keyof T> (obj: T, prop: K): T[K] {
const nativeProp = nativeGetters[prop as string]
if (!nativeProp) {
const props = _.keys(nativeGetters).join(', ')
@@ -245,8 +279,8 @@ const getNativeProp = function (obj, prop) {
return retProp
}
const setNativeProp = function (obj, prop, val) {
const nativeProp = nativeSetters[prop]
const setNativeProp = function<T, K extends keyof T> (obj: T, prop: K, val) {
const nativeProp = nativeSetters[prop as string]
if (!nativeProp) {
const fns = _.keys(nativeSetters).join(', ')
@@ -263,7 +297,11 @@ const setNativeProp = function (obj, prop, val) {
return retProp
}
const isNeedSingleValueChangeInputElement = (el) => {
export interface HTMLSingleValueChangeInputElement extends HTMLInputElement {
type: 'date' | 'time' | 'week' | 'month'
}
const isNeedSingleValueChangeInputElement = (el: HTMLElement): el is HTMLSingleValueChangeInputElement => {
if (!isInput(el)) {
return false
}
@@ -271,7 +309,8 @@ const isNeedSingleValueChangeInputElement = (el) => {
return inputTypeNeedSingleValueChangeRe.test(el.type)
}
const canSetSelectionRangeElement = (el) => {
const canSetSelectionRangeElement = (el): el is HTMLElementCanSetSelectionRange => {
//TODO: If IE, all inputs can set selection range
return isTextarea(el) || (isInput(el) && canSetSelectionRangeElementRe.test(getNativeProp(el, 'type')))
}
@@ -285,23 +324,23 @@ const getTagName = (el) => {
// should be true for elements:
// - with [contenteditable]
// - with document.designMode = 'on'
const isContentEditable = (el) => {
const isContentEditable = (el: any): el is HTMLContentEditableElement => {
return getNativeProp(el, 'isContentEditable')
}
const isTextarea = (el) => {
const isTextarea = (el): el is HTMLTextAreaElement => {
return getTagName(el) === 'textarea'
}
const isInput = (el) => {
const isInput = (el): el is HTMLInputElement => {
return getTagName(el) === 'input'
}
const isButton = (el) => {
const isButton = (el): el is HTMLButtonElement => {
return getTagName(el) === 'button'
}
const isSelect = (el) => {
const isSelect = (el): el is HTMLSelectElement => {
return getTagName(el) === 'select'
}
@@ -313,7 +352,7 @@ const isOptgroup = (el) => {
return getTagName(el) === 'optgroup'
}
const isBody = (el) => {
const isBody = (el): el is HTMLBodyElement => {
return getTagName(el) === 'body'
}
@@ -325,7 +364,7 @@ const isHTML = (el) => {
return getTagName(el) === 'html'
}
const isSvg = function (el) {
const isSvg = function (el): el is SVGElement {
try {
return 'ownerSVGElement' in el
} catch (error) {
@@ -336,7 +375,7 @@ const isSvg = function (el) {
// active element is the default if its null
// or its equal to document.body
const activeElementIsDefault = (activeElement, body) => {
return (!activeElement) || (activeElement === body)
return !activeElement || activeElement === body
}
const isFocused = (el) => {
@@ -375,9 +414,11 @@ const isFocusedOrInFocused = (el) => {
if (elToCheckCurrentlyFocused && elToCheckCurrentlyFocused === activeElement) {
return true
}
return false
}
const isElement = function (obj) {
const isElement = function (obj): obj is HTMLElement | JQuery<HTMLElement> {
try {
if ($jquery.isJquery(obj)) {
obj = obj[0]
@@ -389,10 +430,24 @@ const isElement = function (obj) {
}
}
const isFocusable = ($el) => {
return _.some(focusable, (sel) => {
return $el.is(sel)
})
/**
* The element can be activeElement, receive focus events, and also receive keyboard events
*/
const isFocusable = ($el: JQuery<Element>) => {
// matches a focusable selector
if (_.some(focusableSelectors, (sel) => $el.is(sel))) {
return true
}
// when document.designMode === 'on' (indicated by truthy isContentEditable)
// the documentElement will be focusable
if (
(isElement($el[0]) && getTagName($el[0]) === 'html' && isContentEditable($el[0]))
) {
return true
}
return false
}
const isW3CRendered = (el) => {
@@ -405,8 +460,15 @@ const isW3CFocusable = (el) => {
return isFocusable(wrap(el)) && isW3CRendered(el)
}
const isType = function ($el, type) {
const el = [].concat($jquery.unwrap($el))[0]
type JQueryOrEl<T extends HTMLElement> = JQuery<T> | T
const isType = function (el: JQueryOrEl<HTMLElement>, type) {
el = ([] as HTMLElement[]).concat($jquery.unwrap(el))[0]
if (!isInput(el) && !isButton(el)) {
return false
}
// NOTE: use DOMElement.type instead of getAttribute('type') since
// <input type="asdf"> will have type="text", and behaves like text type
const elType = (getNativeProp(el, 'type') || '').toLowerCase()
@@ -419,7 +481,7 @@ const isType = function ($el, type) {
}
const isScrollOrAuto = (prop) => {
return (prop === 'scroll') || (prop === 'auto')
return prop === 'scroll' || prop === 'auto'
}
const isAncestor = ($el, $maybeAncestor) => {
@@ -443,7 +505,7 @@ const getFirstCommonAncestor = (el1, el2) => {
const getAllParents = (el) => {
let curEl = el.parentNode
const allParents = []
const allParents: any[] = []
while (curEl) {
allParents.push(curEl)
@@ -457,7 +519,7 @@ const isChild = ($el, $maybeChild) => {
return $el.children().index($maybeChild) >= 0
}
const isSelector = ($el, selector) => {
const isSelector = ($el: JQuery<HTMLElement>, selector) => {
return $el.is(selector)
}
@@ -498,7 +560,7 @@ const isAttached = function ($el) {
// is technically bound to a differnet document
// but c'mon
const isIn = (el) => {
return $.contains(doc, el)
return $.contains((doc as unknown) as Element, el)
}
// make sure the document is currently
@@ -529,15 +591,53 @@ const isSame = function ($el1, $el2) {
return el1 && el2 && _.isEqual(el1, el2)
}
const isTextLike = function ($el) {
export interface HTMLContentEditableElement extends HTMLElement {
isContenteditable: true
}
export interface HTMLTextLikeInputElement extends HTMLInputElement {
type:
| 'text'
| 'password'
| 'email'
| 'number'
| 'date'
| 'week'
| 'month'
| 'time'
| 'datetime'
| 'datetime-local'
| 'search'
| 'url'
| 'tel'
setSelectionRange: HTMLInputElement['setSelectionRange']
}
export interface HTMLElementCanSetSelectionRange extends HTMLElement {
setSelectionRange: HTMLInputElement['setSelectionRange']
value: HTMLInputElement['value']
selectionStart: number
selectionEnd: number
}
export type HTMLTextLikeElement = HTMLTextAreaElement | HTMLTextLikeInputElement | HTMLContentEditableElement
const isTextLike = function (el: HTMLElement): el is HTMLTextLikeElement {
const $el = $jquery.wrap(el)
const sel = (selector) => {
return isSelector($el, selector)
}
const type = (type) => {
return isType($el, type)
if (isInput(el)) {
return isType(el, type)
}
return false
}
const isContentEditableElement = isContentEditable($el.get(0))
const isContentEditableElement = isContentEditable(el)
if (isContentEditableElement) return true
return _.some([
isContentEditableElement,
@@ -636,7 +736,7 @@ const isDescendent = ($el1, $el2) => {
return false
}
return !!(($el1.get(0) === $el2.get(0)) || $el1.has($el2).length)
return !!($el1.get(0) === $el2.get(0) || $el1.has($el2).length)
}
const findParent = (el, fn) => {
@@ -664,7 +764,7 @@ const findParent = (el, fn) => {
// 2. check to figure out the element listed at those coordinates
// 3. if this element is ourself or our descendants, click whatever was returned
// 4. else throw an error because something is covering us up
const getFirstFocusableEl = ($el) => {
const getFirstFocusableEl = ($el: JQuery<HTMLElement>) => {
if (isFocusable($el)) {
return $el
}
@@ -856,8 +956,15 @@ const stringify = (el, form = 'long') => {
const $el = $jquery.wrap(el)
const long = () => {
const str = $el.clone().empty().prop('outerHTML')
const text = _.chain($el.text()).clean().truncate({ length: 10 }).value()
const str = $el
.clone()
.empty()
.prop('outerHTML')
const text = (_.chain($el.text()) as any)
.clean()
.truncate({ length: 10 })
.value()
const children = $el.children().length
if (children) {
@@ -903,96 +1010,50 @@ const stringify = (el, form = 'long') => {
})
}
// We extend `module.exports` to allow circular dependencies using `require`
// Otherwise we would not be able to `require` this util from `./selection`, for example.
_.extend(module.exports, {
export {
isElement,
isSelector,
isScrollOrAuto,
isFocusable,
isW3CFocusable,
isAttached,
isDetached,
isAttachedEl,
isDetachedEl,
isAncestor,
isChild,
isScrollable,
isTextLike,
isDescendent,
isContentEditable,
isSame,
isOption,
isOptgroup,
isBody,
isHTML,
isInput,
isIframe,
isTextarea,
isType,
isFocused,
isFocusedOrInFocused,
isInputAllowingImplicitFormSubmission,
isNeedSingleValueChangeInputElement,
canSetSelectionRangeElement,
stringify,
getNativeProp,
setNativeProp,
callNativeMethod,
tryCallNativeMethod,
findParent,
getElements,
getFirstFocusableEl,
getActiveElByDocument,
getContainsSelector,
getFirstDeepestElement,
getFirstCommonAncestor,
getFirstParentWithTagName,
getFirstFixedOrStickyPositionParent,
getFirstStickyPositionParent,
getFirstScrollableParent,
})
}

View File

@@ -11,7 +11,7 @@ const { wrap, unwrap, isJquery, query } = $jquery
const { isVisible, isHidden, getReasonIsHidden } = $visibility
const { isType, isFocusable, isElement, isScrollable, isFocused, stringify, getElements, getContainsSelector, getFirstDeepestElement, isDetached, isAttached, isTextLike, isSelector, isDescendent, getFirstFixedOrStickyPositionParent, getFirstStickyPositionParent, getFirstScrollableParent } = $elements
const { getCoordsByPosition, getElementPositioning, getElementCoordinatesByPosition, getElementAtPointFromViewport, getElementCoordinatesByPositionRelativeToXY } = $coordinates
const { getHostContenteditable, getSelectionBounds } = require('./selection')
const isDom = (obj) => {
return isElement(obj) || isWindow(obj) || isDocument(obj)
}
@@ -23,69 +23,38 @@ const isDom = (obj) => {
// can be tucked away behind these interfaces.
module.exports = {
wrap,
query,
unwrap,
isDom,
isType,
isVisible,
isHidden,
isFocusable,
isTextLike,
isScrollable,
isFocused,
isDetached,
isAttached,
isSelector,
isDescendent,
isElement,
isDocument,
isWindow,
isJquery,
stringify,
getElements,
getContainsSelector,
getFirstDeepestElement,
getWindowByElement,
getReasonIsHidden,
getFirstScrollableParent,
getFirstFixedOrStickyPositionParent,
getFirstStickyPositionParent,
getCoordsByPosition,
getElementPositioning,
getElementAtPointFromViewport,
getElementCoordinatesByPosition,
getElementCoordinatesByPositionRelativeToXY,
getHostContenteditable,
getSelectionBounds,
}

View File

@@ -1,13 +1,5 @@
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const $document = require('./document')
const $elements = require('./elements')
import * as $document from './document'
import * as $elements from './elements'
const INTERNAL_STATE = '__Cypress_state__'
@@ -222,6 +214,8 @@ const deleteRightOfCursor = function (el) {
//# successful delete
return true
}
return false
}
const deleteLeftOfCursor = function (el) {
@@ -257,6 +251,8 @@ const deleteLeftOfCursor = function (el) {
//# successful delete
return true
}
return false
}
const _collapseInputOrTextArea = (el, toIndex) => {
@@ -587,7 +583,7 @@ const interceptSelect = function () {
// el = el.firstChild
// return el
module.exports = {
export {
getSelectionBounds,
deleteRightOfCursor,
deleteLeftOfCursor,

View File

@@ -1,7 +1,6 @@
const $ = Cypress.$.bind(Cypress)
const { _ } = Cypress
const { Promise } = Cypress
const $selection = require('../../../../../src/dom/selection')
const { getCommandLogWithText, findReactInstance, withMutableReporterState } = require('../../../support/utils')
// trim new lines at the end of innerText
@@ -3209,7 +3208,7 @@ describe('src/cy/commands/actions/type', () => {
const hostEl = cy.$$('<div contenteditable><div id="ce-inner1">foo</div></div>').appendTo(cy.$$('body'))
cy.get('#ce-inner1').then(($el) => {
expect($selection.getHostContenteditable($el[0])).to.eq(hostEl[0])
expect(Cypress.dom.getHostContenteditable($el[0])).to.eq(hostEl[0])
})
})
@@ -3217,7 +3216,7 @@ describe('src/cy/commands/actions/type', () => {
const hostEl = cy.$$('<div contenteditable="true"><div id="ce-inner1">foo</div></div>').appendTo(cy.$$('body'))
cy.get('#ce-inner1').then(($el) => {
expect($selection.getHostContenteditable($el[0])).to.eq(hostEl[0])
expect(Cypress.dom.getHostContenteditable($el[0])).to.eq(hostEl[0])
})
})
@@ -3225,7 +3224,7 @@ describe('src/cy/commands/actions/type', () => {
const hostEl = cy.$$('<div contenteditable=""><div id="ce-inner1">foo</div></div>').appendTo(cy.$$('body'))
cy.get('#ce-inner1').then(($el) => {
expect($selection.getHostContenteditable($el[0])).to.eq(hostEl[0])
expect(Cypress.dom.getHostContenteditable($el[0])).to.eq(hostEl[0])
})
})
@@ -3233,7 +3232,7 @@ describe('src/cy/commands/actions/type', () => {
const hostEl = cy.$$('<div contenteditable="foo"><div id="ce-inner1">foo</div></div>').appendTo(cy.$$('body'))
cy.get('#ce-inner1').then(($el) => {
expect($selection.getHostContenteditable($el[0])).to.eq(hostEl[0])
expect(Cypress.dom.getHostContenteditable($el[0])).to.eq(hostEl[0])
})
})
@@ -3241,7 +3240,7 @@ describe('src/cy/commands/actions/type', () => {
cy.$$('<div contenteditable="false"><div id="ce-inner1">foo</div></div>').appendTo(cy.$$('body'))
cy.get('#ce-inner1').then(($el) => {
expect($selection.getHostContenteditable($el[0])).to.eq($el[0])
expect(Cypress.dom.getHostContenteditable($el[0])).to.eq($el[0])
})
})
@@ -3300,7 +3299,7 @@ describe('src/cy/commands/actions/type', () => {
cy.get('input:first').invoke('attr', 'maxlength', '5').type('foobar{leftarrow}')
cy.window().then((win) => {
expect($selection.getSelectionBounds(Cypress.$('input:first').get(0)))
expect(Cypress.dom.getSelectionBounds(Cypress.$('input:first').get(0)))
.to.deep.eq({ start: 4, end: 4 })
})
})
@@ -3309,7 +3308,7 @@ describe('src/cy/commands/actions/type', () => {
cy.get('input:first').type('foo{rightarrow}{rightarrow}{rightarrow}bar{rightarrow}')
cy.window().then((win) => {
expect($selection.getSelectionBounds(Cypress.$('input:first').get(0)))
expect(Cypress.dom.getSelectionBounds(Cypress.$('input:first').get(0)))
.to.deep.eq({ start: 6, end: 6 })
})
})
@@ -3318,7 +3317,7 @@ describe('src/cy/commands/actions/type', () => {
cy.get('input:first').type(`oo{leftarrow}{leftarrow}{leftarrow}f${'{leftarrow}'.repeat(5)}`)
cy.window().then((win) => {
expect($selection.getSelectionBounds(Cypress.$('input:first').get(0)))
expect(Cypress.dom.getSelectionBounds(Cypress.$('input:first').get(0)))
.to.deep.eq({ start: 0, end: 0 })
})
})
@@ -3327,7 +3326,7 @@ describe('src/cy/commands/actions/type', () => {
cy.get('[contenteditable]:first').type('foobar')
cy.window().then((win) => {
expect($selection.getSelectionBounds(Cypress.$('[contenteditable]:first').get(0)))
expect(Cypress.dom.getSelectionBounds(Cypress.$('[contenteditable]:first').get(0)))
.to.deep.eq({ start: 6, end: 6 })
})
})
@@ -3340,7 +3339,7 @@ describe('src/cy/commands/actions/type', () => {
cy.get('[contenteditable]:first').type('bar')
cy.window().then((win) => {
expect($selection.getSelectionBounds(Cypress.$('[contenteditable]:first').get(0)))
expect(Cypress.dom.getSelectionBounds(Cypress.$('[contenteditable]:first').get(0)))
.to.deep.eq({ start: 6, end: 6 })
})
})
@@ -3349,7 +3348,7 @@ describe('src/cy/commands/actions/type', () => {
cy.get('[contenteditable]:first').type('foo{leftarrow}{leftarrow}')
cy.window().then((win) => {
expect($selection.getSelectionBounds(Cypress.$('[contenteditable]:first').get(0)))
expect(Cypress.dom.getSelectionBounds(Cypress.$('[contenteditable]:first').get(0)))
.to.deep.eq({ start: 1, end: 1 })
})
})
@@ -3364,7 +3363,7 @@ describe('src/cy/commands/actions/type', () => {
cy.get(':text:first').type('foobar')
cy.window().then((win) => {
expect($selection.getSelectionBounds(Cypress.$(':text:first').get(0)))
expect(Cypress.dom.getSelectionBounds(Cypress.$(':text:first').get(0)))
.to.deep.eq({ start: 6, end: 6 })
})
})
@@ -3373,7 +3372,7 @@ describe('src/cy/commands/actions/type', () => {
cy.get('#comments').type('foobar')
cy.window().then((win) => {
expect($selection.getSelectionBounds(Cypress.$('#comments').get(0)))
expect(Cypress.dom.getSelectionBounds(Cypress.$('#comments').get(0)))
.to.deep.eq({ start: 6, end: 6 })
})
})

View File

@@ -24,9 +24,9 @@ describe "src/cy/commands/connectors", ->
it "spreads a jQuery wrapper into individual arguments", ->
cy.noop($("div")).spread (first, second) ->
expect(first.tagName).to.eq('DIV');
expect(first.tagName).to.eq('DIV')
expect(first.innerText).to.eq("div")
expect(second.tagName).to.eq('DIV');
expect(second.tagName).to.eq('DIV')
expect(second.innerText).to.contain("Nested Find")
it "passes timeout option to spread", ->
@@ -480,8 +480,8 @@ describe "src/cy/commands/connectors", ->
memo + num
, 0
math: {
sum: =>
@obj.sum.apply(@obj, arguments)
sum: (args...) =>
@obj.sum.apply(@obj, args)
}
}
@@ -1222,7 +1222,7 @@ describe "src/cy/commands/connectors", ->
it "awaits promises returned", ->
count = 0
start = new Date
start = new Date()
cy.get("#list li").each ($li, i, arr) ->
new Promise (resolve, reject) ->
@@ -1232,7 +1232,7 @@ describe "src/cy/commands/connectors", ->
, 20
.then ($lis) ->
expect(count).to.eq(3)
expect(new Date - start).to.be.gt(60)
expect(new Date() - start).to.be.gt(60)
it "supports array like structures", ->
count = 0

View File

@@ -1,9 +1,17 @@
// only required to read in webpack config, since it is .ts
require('@packages/ts/register')
const _ = require('lodash')
const path = require('path')
const fs = require('fs-extra')
const Promise = require('bluebird')
const webpack = require('@cypress/webpack-preprocessor')
const webpackOptions = require('@packages/runner/webpack.config.ts').default
module.exports = (on) => {
on('file:preprocessor', webpack({ webpackOptions }))
on('task', {
'return:arg' (arg) {
return arg

5
packages/driver/ts/import-coffee.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
// allows Typescript to import .coffee files
declare module '*.coffee' {
const content: any
export default content
}

View File

@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"allowJs": true,
"noImplicitAny": false,
"noImplicitThis": false,
"strictFunctionTypes": true,
"preserveWatchOutput": true,
"sourceMap": true,
"importHelpers": true,
"strictNullChecks": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"noEmit": true,
"noImplicitReturns": true,
"allowSyntheticDefaultImports": true,
"outDir": "dist",
"esModuleInterop": true,
"noErrorTruncation": true
}
}

View File

@@ -12,7 +12,7 @@ describe('browser detection', () => {
// using DEBUG=... flag
const checkBrowsers = (browsers) => {
log('detected browsers %j', browsers)
expect(browsers).to.be.an.array
expect(browsers).to.be.an('array')
const mainProps = project(['name', 'version'], browsers)

View File

@@ -4,8 +4,8 @@ const api = require('../..')
describe('launcher', function () {
it('has all needed methods', function () {
expect(api.launch).to.be.a.function
expect(api.detect).to.be.a.function
expect(api.detectByPath).to.be.a.function
expect(api.launch).to.be.a('function')
expect(api.detect).to.be.a('function')
expect(api.detectByPath).to.be.a('function')
})
})

View File

@@ -1,6 +0,0 @@
{
"extends": "tslint-config-standard",
"rules": {
"space-before-function-paren": false
}
}

10
packages/network/index.ts Normal file
View File

@@ -0,0 +1,10 @@
if (process.env.CYPRESS_ENV !== 'production') {
require('@packages/ts/register')
}
import agent from './lib/agent'
import * as connect from './lib/connect'
export { agent }
export { connect }

View File

@@ -1,4 +1,4 @@
import { $, dom } from '@packages/driver'
import { dom } from '@packages/driver'
import events from './events'
class Shortcuts {
@@ -12,7 +12,7 @@ class Shortcuts {
_handleKeyDownEvent (event) {
// if typing into an input, textarea, etc, don't trigger any shortcuts
if (dom.isTextLike($(event.target))) return
if (dom.isTextLike(event.target)) return
switch (event.key) {
case 'r': events.emit('restart')

View File

@@ -60,7 +60,7 @@ export default class Header extends Component {
<p>The <strong>viewport</strong> determines the width and height of your application. By default the viewport will be <strong>{state.defaults.width}px</strong> by <strong>{state.defaults.height}px</strong> unless specified by a <code>cy.viewport</code> command.</p>
<p>Additionally you can override the default viewport dimensions by specifying these values in your {configFileFormatted(config.configFile)}.</p>
<pre>{/* eslint-disable indent */}
{`{
{`{
"viewportWidth": ${state.defaults.width},
"viewportHeight": ${state.defaults.height}
}`}

5
packages/runner/ts/import-coffee.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
// allows Typescript to import .coffee files
declare module '*.coffee' {
const content: any
export default content
}

View File

@@ -22,7 +22,7 @@ if (liveReloadEnabled && watchModeEnabled) console.log(chalk.gray(`\nLive Reload
process.env.NODE_ENV = env
const config: webpack.Configuration = {
const commonConfig: webpack.Configuration = {
mode: 'none',
node: {
fs: 'empty',
@@ -191,9 +191,8 @@ const config: webpack.Configuration = {
],
cache: true,
}
export default config
export default commonConfig
export { HtmlWebpackPlugin }