')
- .css(css)
- .attr('data-top', dimensions.top)
- .attr('data-left', dimensions.left)
- .attr('data-layer', attr)
- .prependTo(container)
-}
-
-function dimensionsMatchPreviousLayer (obj, container) {
- // since we're prepending to the container that
- // means the previous layer is actually the first child element
- const previousLayer = container.children().first().get(0)
-
- // bail if there is no previous layer
- if (!previousLayer) {
- return
- }
-
- return obj.width === previousLayer.offsetWidth &&
- obj.height === previousLayer.offsetHeight
-}
-
-function getDimensionsFor (dimensions, attr, dimension) {
- return dimensions[`${dimension}With${attr}`]
-}
-
-function getZIndex (el) {
- if (/^(auto|0)$/.test(el.css('zIndex'))) {
- return 2147483647
- }
-
- return _.toNumber(el.css('zIndex'))
-}
-
-function getElementDimensions ($el) {
- const el = $el.get(0)
-
- const { offsetHeight, offsetWidth } = el
-
- const box = {
- offset: $el.offset(), // offset disregards margin but takes into account border + padding
- // dont use jquery here for width/height because it uses getBoundingClientRect() which returns scaled values.
- // TODO: switch back to using jquery when upgrading to jquery 3.4+
- paddingTop: getPadding($el, 'top'),
- paddingRight: getPadding($el, 'right'),
- paddingBottom: getPadding($el, 'bottom'),
- paddingLeft: getPadding($el, 'left'),
- borderTop: getBorder($el, 'top'),
- borderRight: getBorder($el, 'right'),
- borderBottom: getBorder($el, 'bottom'),
- borderLeft: getBorder($el, 'left'),
- marginTop: getMargin($el, 'top'),
- marginRight: getMargin($el, 'right'),
- marginBottom: getMargin($el, 'bottom'),
- marginLeft: getMargin($el, 'left'),
- }
-
- // NOTE: offsetWidth/height always give us content + padding + border, so subtract them
- // to get the true "clientHeight" and "clientWidth".
- // we CANNOT just use "clientHeight" and "clientWidth" because those always return 0
- // for inline elements >_<
- //
- box.width = offsetWidth - (box.paddingLeft + box.paddingRight + box.borderLeft + box.borderRight)
- box.height = offsetHeight - (box.paddingTop + box.paddingBottom + box.borderTop + box.borderBottom)
-
- // innerHeight: Get the current computed height for the first
- // element in the set of matched elements, including padding but not border.
-
- // outerHeight: Get the current computed height for the first
- // element in the set of matched elements, including padding, border,
- // and optionally margin. Returns a number (without 'px') representation
- // of the value or null if called on an empty set of elements.
- box.heightWithPadding = box.height + box.paddingTop + box.paddingBottom
-
- box.heightWithBorder = box.heightWithPadding + box.borderTop + box.borderBottom
-
- box.heightWithMargin = box.heightWithBorder + box.marginTop + box.marginBottom
-
- box.widthWithPadding = box.width + box.paddingLeft + box.paddingRight
-
- box.widthWithBorder = box.widthWithPadding + box.borderLeft + box.borderRight
-
- box.widthWithMargin = box.widthWithBorder + box.marginLeft + box.marginRight
-
- return box
-}
-
-function getNumAttrValue ($el, attr) {
- // nuke anything thats not a number or a negative symbol
- const num = _.toNumber($el.css(attr).replace(/[^0-9\.-]+/, ''))
-
- if (!_.isFinite(num)) {
- throw new Error('Element attr did not return a valid number')
- }
-
- return num
-}
-
-function getPadding ($el, dir) {
- return getNumAttrValue($el, `padding-${dir}`)
-}
-
-function getBorder ($el, dir) {
- return getNumAttrValue($el, `border-${dir}-width`)
-}
-
-function getMargin ($el, dir) {
- return getNumAttrValue($el, `margin-${dir}`)
-}
-
-function getOuterSize ($el) {
- return {
- width: $el.outerWidth(true),
- height: $el.outerHeight(true),
- }
-}
-
-function isInViewport (win, el) {
- let rect = el.getBoundingClientRect()
-
- return (
- rect.top >= 0 &&
- rect.left >= 0 &&
- rect.bottom <= win.innerHeight &&
- rect.right <= win.innerWidth
- )
-}
-
-function scrollIntoView (win, el) {
- if (!el || isInViewport(win, el)) return
-
- el.scrollIntoView()
-}
-
-const sizzleRe = /sizzle/i
-
-function getElementsForSelector ({ $root, selector, method, cypressDom }) {
- let $el = null
-
- try {
- if (method === 'contains') {
- $el = $root.find(cypressDom.getContainsSelector(selector))
- if ($el.length) {
- $el = cypressDom.getFirstDeepestElement($el)
- }
- } else {
- $el = $root.find(selector)
- }
- } catch (err) {
- // if not a sizzle error, ignore it and let $el be null
- if (!sizzleRe.test(err.stack)) throw err
- }
-
- return $el
-}
-
-function addCssAnimationDisabler ($body) {
- $(`
-
- `).appendTo($body)
-}
-
-function removeCssAnimationDisabler ($body) {
- $body.find('#__cypress-animation-disabler').remove()
-}
-
-function addBlackoutForElement ($body, $el) {
- const dimensions = getElementDimensions($el)
- const width = dimensions.widthWithBorder
- const height = dimensions.heightWithBorder
- const top = dimensions.offset.top
- const left = dimensions.offset.left
-
- const style = styles(`
- ${resetStyles}
- position: absolute;
- top: ${top}px;
- left: ${left}px;
- width: ${width}px;
- height: ${height}px;
- background-color: black;
- z-index: 2147483647;
- `)
-
- $(`
`).appendTo($body)
-}
-
-function addBlackout ($body, selector) {
- let $el
-
- try {
- $el = $body.find(selector)
- if (!$el.length) return
- } catch (err) {
- // if it's an invalid selector, just ignore it
- return
- }
-
- $el.each(function () {
- addBlackoutForElement($body, $(this))
- })
-}
-
-function removeBlackouts ($body) {
- $body.find('.__cypress-blackout').remove()
-}
-
-export default {
- addBlackout,
- removeBlackouts,
- addElementBoxModelLayers,
- addHitBoxLayer,
- addOrUpdateSelectorPlaygroundHighlight,
- addCssAnimationDisabler,
- removeCssAnimationDisabler,
- getElementsForSelector,
- getOuterSize,
- scrollIntoView,
-}
diff --git a/packages/runner-ct/src/lib/event-manager.js b/packages/runner-ct/src/lib/event-manager.js
deleted file mode 100644
index 5ce3cc772b..0000000000
--- a/packages/runner-ct/src/lib/event-manager.js
+++ /dev/null
@@ -1,491 +0,0 @@
-import _ from 'lodash'
-import { EventEmitter } from 'events'
-import Promise from 'bluebird'
-import { action } from 'mobx'
-
-import { client } from '@packages/socket'
-
-import automation from './automation'
-import logger from './logger'
-
-import $Cypress, { $ } from '@packages/driver'
-
-const ws = client.connect({
- path: '/__socket.io',
- transports: ['websocket'],
-})
-
-ws.on('connect', () => {
- ws.emit('runner:connected')
-})
-
-const driverToReporterEvents = 'paused before:firefox:force:gc after:firefox:force:gc'.split(' ')
-const driverToLocalAndReporterEvents = 'run:start run:end'.split(' ')
-const driverToSocketEvents = 'backend:request automation:request mocha recorder:frame'.split(' ')
-const driverTestEvents = 'test:before:run:async test:after:run'.split(' ')
-const driverToLocalEvents = 'viewport:changed config stop url:changed page:loading visit:failed'.split(' ')
-const socketRerunEvents = 'runner:restart watched:file:changed'.split(' ')
-const socketToDriverEvents = 'net:event script:error'.split(' ')
-
-const localBus = new EventEmitter()
-const reporterBus = new EventEmitter()
-
-// NOTE: this is exposed for testing, ideally we should only expose this if a test flag is set
-window.runnerWs = ws
-
-// NOTE: this is for testing Cypress-in-Cypress, window.Cypress is undefined here
-// unless Cypress has been loaded into the AUT frame
-if (window.Cypress) {
- window.eventManager = { reporterBus, localBus }
-}
-
-/**
- * @type {Cypress.Cypress}
- */
-let Cypress
-
-const eventManager = {
- reporterBus,
-
- getCypress () {
- return Cypress
- },
-
- addGlobalListeners (state, connectionInfo) {
- const rerun = () => {
- if (!this) {
- // if the tests have been reloaded
- // then nothing to rerun
- return
- }
-
- return this._reRun(state)
- }
-
- ws.emit('is:automation:client:connected', connectionInfo, action('automationEnsured', (isConnected) => {
- state.automation = isConnected ? automation.CONNECTED : automation.MISSING
- ws.on('automation:disconnected', action('automationDisconnected', () => {
- state.automation = automation.DISCONNECTED
- }))
- }))
-
- ws.on('change:to:url', (url) => {
- window.location.href = url
- })
-
- ws.on('automation:push:message', (msg, data = {}) => {
- if (!Cypress) return
-
- switch (msg) {
- case 'change:cookie':
- Cypress.Cookies.log(data.message, data.cookie, data.removed)
- break
- default:
- break
- }
- })
-
- ws.on('component:specs:changed', (specs) => {
- state.setSpecs(specs)
- })
-
- ws.on('dev-server:hmr:error', (error) => {
- Cypress.stop()
- localBus.emit('script:error', error)
- })
-
- _.each(socketRerunEvents, (event) => {
- ws.on(event, rerun)
- })
-
- _.each(socketToDriverEvents, (event) => {
- ws.on(event, (...args) => {
- Cypress.emit(event, ...args)
- })
- })
-
- const logCommand = (logId) => {
- const consoleProps = Cypress.runner.getConsolePropsForLogById(logId)
-
- logger.logFormatted(consoleProps)
- }
-
- reporterBus.on('runner:console:error', ({ err, commandId }) => {
- if (!Cypress) return
-
- if (commandId || err) logger.clearLog()
-
- if (commandId) logCommand(commandId)
-
- if (err) logger.logError(err.stack)
- })
-
- reporterBus.on('runner:console:log', (logId) => {
- if (!Cypress) return
-
- logger.clearLog()
- logCommand(logId)
- })
-
- reporterBus.on('focus:tests', this.focusTests)
-
- reporterBus.on('get:user:editor', (cb) => {
- ws.emit('get:user:editor', cb)
- })
-
- reporterBus.on('set:user:editor', (editor) => {
- ws.emit('set:user:editor', editor)
- })
-
- reporterBus.on('runner:restart', rerun)
-
- function sendEventIfSnapshotProps (logId, event) {
- if (!Cypress) return
-
- const snapshotProps = Cypress.runner.getSnapshotPropsForLogById(logId)
-
- if (snapshotProps) {
- localBus.emit(event, snapshotProps)
- }
- }
-
- reporterBus.on('runner:show:snapshot', (logId) => {
- sendEventIfSnapshotProps(logId, 'show:snapshot')
- })
-
- reporterBus.on('runner:hide:snapshot', this._hideSnapshot.bind(this))
-
- reporterBus.on('runner:pin:snapshot', (logId) => {
- sendEventIfSnapshotProps(logId, 'pin:snapshot')
- })
-
- reporterBus.on('runner:unpin:snapshot', this._unpinSnapshot.bind(this))
-
- reporterBus.on('runner:resume', () => {
- if (!Cypress) return
-
- Cypress.emit('resume:all')
- })
-
- reporterBus.on('runner:next', () => {
- if (!Cypress) return
-
- Cypress.emit('resume:next')
- })
-
- reporterBus.on('runner:stop', () => {
- if (!Cypress) return
-
- Cypress.stop()
- })
-
- reporterBus.on('save:state', (state) => {
- this.saveState(state)
- })
-
- reporterBus.on('external:open', (url) => {
- ws.emit('external:open', url)
- })
-
- reporterBus.on('open:file', (url) => {
- ws.emit('open:file', url)
- })
-
- const $window = $(window)
-
- // when we actually unload then
- // nuke all of the cookies again
- // so we clear out unload
- $window.on('unload', () => {
- this._clearAllCookies()
- })
-
- // when our window triggers beforeunload
- // we know we've change the URL and we need
- // to clear our cookies
- // additionally we set unload to true so
- // that Cypress knows not to set any more
- // cookies
- $window.on('beforeunload', () => {
- reporterBus.emit('reporter:restart:test:run')
-
- this._clearAllCookies()
- this._setUnload()
- })
- },
-
- start (config) {
- if (config.socketId) {
- ws.emit('app:connect', config.socketId)
- }
- },
-
- setup (config) {
- Cypress = this.Cypress = $Cypress.create(config)
-
- // expose Cypress globally
- // since CT AUT shares the window with the spec, we don't want to overwrite
- // our spec Cypress instance with the component's Cypress instance
- if (window.top === window) {
- window.Cypress = Cypress
- }
-
- this._addCypressListeners(Cypress)
-
- ws.emit('watch:test:file', config.spec)
- },
-
- isBrowser (browserName) {
- if (!this.Cypress) return false
-
- return this.Cypress.isBrowser(browserName)
- },
-
- initialize ($autIframe, config) {
- performance.mark('initialize-start')
-
- return Cypress.initialize({
- $autIframe,
- onSpecReady: () => {
- // get the current runnable in case we reran mid-test due to a visit
- // to a new domain
- ws.emit('get:existing:run:state', (state = {}) => {
- if (!Cypress.runner) {
- // the tests have been reloaded
- return
- }
-
- const runnables = Cypress.runner.normalizeAll(state.tests)
- const run = () => {
- performance.mark('initialize-end')
- performance.measure('initialize', 'initialize-start', 'initialize-end')
-
- this._runDriver(state)
- }
-
- reporterBus.emit('runnables:ready', runnables)
-
- if (state.numLogs) {
- Cypress.runner.setNumLogs(state.numLogs)
- }
-
- if (state.startTime) {
- Cypress.runner.setStartTime(state.startTime)
- }
-
- if (config.isTextTerminal && !state.currentId) {
- // we are in run mode and it's the first load
- // store runnables in backend and maybe send to dashboard
- return ws.emit('set:runnables:and:maybe:record:tests', runnables, run)
- }
-
- if (state.currentId) {
- // if we have a currentId it means
- // we need to tell the Cypress to skip
- // ahead to that test
- Cypress.runner.resumeAtTest(state.currentId, state.emissions)
- }
-
- run()
- })
- },
- })
- },
-
- _addCypressListeners (Cypress) {
- Cypress.on('message', (msg, data, cb) => {
- ws.emit('client:request', msg, data, cb)
- })
-
- _.each(driverToSocketEvents, (event) => {
- Cypress.on(event, (...args) => {
- return ws.emit(event, ...args)
- })
- })
-
- Cypress.on('collect:run:state', () => {
- if (Cypress.env('NO_COMMAND_LOG')) {
- return Promise.resolve()
- }
-
- return new Promise((resolve) => {
- reporterBus.emit('reporter:collect:run:state', resolve)
- })
- })
-
- Cypress.on('log:added', (log) => {
- const displayProps = Cypress.runner.getDisplayPropsForLog(log)
-
- reporterBus.emit('reporter:log:add', displayProps)
- })
-
- Cypress.on('log:changed', (log) => {
- const displayProps = Cypress.runner.getDisplayPropsForLog(log)
-
- reporterBus.emit('reporter:log:state:changed', displayProps)
- })
-
- Cypress.on('before:screenshot', (config, cb) => {
- const beforeThenCb = () => {
- localBus.emit('before:screenshot', config)
- cb()
- }
-
- if (Cypress.env('NO_COMMAND_LOG')) {
- return beforeThenCb()
- }
-
- const wait = !config.appOnly && config.waitForCommandSynchronization
-
- if (!config.appOnly) {
- reporterBus.emit('test:set:state', _.pick(config, 'id', 'isOpen'), wait ? beforeThenCb : undefined)
- }
-
- if (!wait) beforeThenCb()
- })
-
- Cypress.on('after:screenshot', (config) => {
- localBus.emit('after:screenshot', config)
- })
-
- _.each(driverToReporterEvents, (event) => {
- Cypress.on(event, (...args) => {
- reporterBus.emit(event, ...args)
- })
- })
-
- _.each(driverTestEvents, (event) => {
- Cypress.on(event, (test, cb) => {
- reporterBus.emit(event, test, cb)
- })
- })
-
- _.each(driverToLocalAndReporterEvents, (event) => {
- Cypress.on(event, (...args) => {
- localBus.emit(event, ...args)
- reporterBus.emit(event, ...args)
- })
- })
-
- _.each(driverToLocalEvents, (event) => {
- Cypress.on(event, (...args) => {
- return localBus.emit(event, ...args)
- })
- })
-
- Cypress.on('script:error', (err) => {
- Cypress.stop()
- localBus.emit('script:error', err)
- })
- },
-
- _runDriver (state) {
- performance.mark('run-s')
- Cypress.run(() => {
- performance.mark('run-e')
- performance.measure('run', 'run-s', 'run-e')
- })
-
- reporterBus.emit('reporter:start', {
- firefoxGcInterval: Cypress.getFirefoxGcInterval(),
- startTime: Cypress.runner.getStartTime(),
- numPassed: state.passed,
- numFailed: state.failed,
- numPending: state.pending,
- autoScrollingEnabled: state.autoScrollingEnabled,
- scrollTop: state.scrollTop,
- })
- },
-
- stop () {
- localBus.removeAllListeners()
- ws.off()
- },
-
- _reRun (state) {
- if (!Cypress) return
-
- state.setIsLoading(true)
-
- // when we are re-running we first
- // need to stop cypress always
- Cypress.stop()
-
- return this._restart()
- .then(() => {
- // this probably isn't 100% necessary
- // since Cypress will fall out of scope
- // but we want to be aggressive here
- // and force GC early and often
- Cypress.removeAllListeners()
-
- localBus.emit('restart')
- })
- },
-
- _restart () {
- return new Promise((resolve) => {
- reporterBus.once('reporter:restarted', resolve)
- reporterBus.emit('reporter:restart:test:run')
- })
- },
-
- emit (event, ...args) {
- localBus.emit(event, ...args)
- },
-
- on (event, ...args) {
- localBus.on(event, ...args)
- },
-
- off (event, ...args) {
- localBus.off(event, ...args)
- },
-
- notifyRunningSpec (specFile) {
- ws.emit('spec:changed', specFile)
- },
-
- focusTests () {
- ws.emit('focus:tests')
- },
-
- snapshotUnpinned () {
- this._unpinSnapshot()
- this._hideSnapshot()
- reporterBus.emit('reporter:snapshot:unpinned')
- },
-
- _unpinSnapshot () {
- localBus.emit('unpin:snapshot')
- },
-
- _hideSnapshot () {
- localBus.emit('hide:snapshot')
- },
-
- launchBrowser (browser) {
- ws.emit('reload:browser', window.location.toString(), browser && browser.name)
- },
-
- // clear all the cypress specific cookies
- // whenever our app starts
- // and additional when we stop running our tests
- _clearAllCookies () {
- if (!Cypress) return
-
- Cypress.Cookies.clearCypressCookies()
- },
-
- _setUnload () {
- if (!Cypress) return
-
- Cypress.Cookies.setCy('unload', true)
- },
-
- saveState (state) {
- ws.emit('save:app:state', state)
- },
-}
-
-export default eventManager
diff --git a/packages/runner-ct/src/lib/state.ts b/packages/runner-ct/src/lib/state.ts
index 110e0ff5ff..157ddd9ca7 100644
--- a/packages/runner-ct/src/lib/state.ts
+++ b/packages/runner-ct/src/lib/state.ts
@@ -1,8 +1,8 @@
import { action, computed, observable } from 'mobx'
import _ from 'lodash'
-import automation from './automation'
import { UIPlugin } from '../plugins/UIPlugin'
import { nanoid } from 'nanoid'
+import { automation } from '@packages/runner-shared'
import {
DEFAULT_REPORTER_WIDTH,
LEFT_NAV_WIDTH,
@@ -21,16 +21,13 @@ interface Defaults {
messageType: string
messageControls: unknown
- width: number
- height: number
-
reporterWidth: number | null
pluginsHeight: number | null
specListWidth: number | null
isSpecsListOpen: boolean
- viewportHeight: number
- viewportWidth: number
+ height: number
+ width: number
url: string
highlightUrl: boolean
@@ -48,11 +45,8 @@ const _defaults: Defaults = {
messageType: '',
messageControls: null,
- width: 500,
height: 500,
-
- viewportHeight: 500,
- viewportWidth: 500,
+ width: 500,
pluginsHeight: PLUGIN_BAR_HEIGHT,
@@ -111,9 +105,6 @@ export default class State {
@observable windowWidth = 0
@observable windowHeight = 0
- @observable viewportWidth = _defaults.viewportWidth
- @observable viewportHeight = _defaults.viewportHeight
-
@observable automation = automation.CONNECTING
@observable.ref scriptError: string | undefined
@@ -175,10 +166,10 @@ export default class State {
return 1
}
- if (autAreaWidth < this.viewportWidth || autAreaHeight < this.viewportHeight) {
+ if (autAreaWidth < this.width || autAreaHeight < this.height) {
return Math.min(
- autAreaWidth / this.viewportWidth,
- autAreaHeight / this.viewportHeight,
+ autAreaWidth / this.width,
+ autAreaHeight / this.height,
)
}
@@ -218,9 +209,9 @@ export default class State {
this.screenshotting = screenshotting
}
- @action updateAutViewportDimensions (dimensions: { viewportWidth: number, viewportHeight: number }) {
- this.viewportHeight = dimensions.viewportHeight
- this.viewportWidth = dimensions.viewportWidth
+ @action updateDimensions (width: number, height: number) {
+ this.height = height
+ this.width = width
}
@action toggleIsSpecsListOpen () {
@@ -249,7 +240,11 @@ export default class State {
this.specListWidth = width
}
- @action updateWindowDimensions ({ windowWidth, windowHeight }: { windowWidth?: number, windowHeight?: number }) {
+ @action updateWindowDimensions ({
+ windowWidth,
+ windowHeight,
+ headerHeight,
+ }: { windowWidth?: number, windowHeight?: number, headerHeight?: number }) {
if (windowWidth) {
this.windowWidth = windowWidth
}
@@ -257,6 +252,10 @@ export default class State {
if (windowHeight) {
this.windowHeight = windowHeight
}
+
+ if (headerHeight) {
+ this.headerHeight = headerHeight
+ }
}
@action clearMessage () {
@@ -330,7 +329,7 @@ export default class State {
}
runMultiMode = async () => {
- const eventManager = require('./event-manager').default
+ const eventManager = require('@packages/runner-shared').eventManager
const waitForRunEnd = () => new Promise((res) => eventManager.on('run:end', res))
this.setSpec(null)
diff --git a/packages/runner-ct/src/main.jsx b/packages/runner-ct/src/main.jsx
index 8821350108..c0ac5634f8 100644
--- a/packages/runner-ct/src/main.jsx
+++ b/packages/runner-ct/src/main.jsx
@@ -4,8 +4,9 @@ import { render } from 'react-dom'
import { utils as driverUtils } from '@packages/driver'
import defaultEvents from '@packages/reporter/src/lib/events'
+import App from './app/RunnerCt'
import State from './lib/state'
-import Container from './app/container'
+import { Container, eventManager } from '@packages/runner-shared'
import util from './lib/util'
// to support async/await
@@ -62,9 +63,20 @@ const Runner = {
Runner.state = state
Runner.configureMobx = configure
- state.updateAutViewportDimensions({ viewportWidth: config.viewportWidth, viewportHeight: config.viewportHeight })
+ state.updateDimensions(config.viewportWidth, config.viewportHeight)
- render(
, el)
+ const container = (
+
+ )
+
+ render(container, el)
})()
},
}
diff --git a/packages/runner-ct/tsconfig.json b/packages/runner-ct/tsconfig.json
index ab62866152..b45f3e0289 100644
--- a/packages/runner-ct/tsconfig.json
+++ b/packages/runner-ct/tsconfig.json
@@ -27,7 +27,7 @@
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
// "traceResolution": true,
- "strict": false,
+ "strict": true,
"forceConsistentCasingInFileNames": true,
/**
* Skip type checking of all declaration files (*.d.ts).
@@ -56,6 +56,8 @@
},
"include": [
"./lib/*.ts",
+ "./src*.ts",
+ "./src*.tsx",
"./index.ts",
"./index.d.ts",
"./../ts/index.d.ts"
diff --git a/packages/runner-ct/webpack.config.ts b/packages/runner-ct/webpack.config.ts
index cf7bea0f2c..e5ef5a8f35 100644
--- a/packages/runner-ct/webpack.config.ts
+++ b/packages/runner-ct/webpack.config.ts
@@ -21,20 +21,31 @@ babelLoader.use.options.plugins.push([require.resolve('babel-plugin-prismjs'), {
css: false,
}])
-let pngRule
-// @ts-ignore
-const nonPngRules = _.filter(commonConfig.module.rules, (rule) => {
- // @ts-ignore
- if (rule.test.toString().includes('png')) {
- pngRule = rule
-
- return false
+const { pngRule, nonPngRules } = commonConfig!.module!.rules!.reduce<{
+ nonPngRules: webpack.RuleSetRule[]
+ pngRule: webpack.RuleSetRule | undefined
+}>((acc, rule) => {
+ if (rule?.test?.toString().includes('png')) {
+ return {
+ ...acc,
+ pngRule: rule,
+ }
}
- return true
+ return {
+ ...acc,
+ nonPngRules: [...acc.nonPngRules, rule],
+ }
+}, {
+ nonPngRules: [],
+ pngRule: undefined,
})
-pngRule.use[0].options = {
+if (!pngRule || !pngRule.use) {
+ throw Error('Could not find png loader')
+}
+
+(pngRule.use as webpack.RuleSetLoader[])[0].options = {
name: '[name].[ext]',
outputPath: 'img',
publicPath: '/__cypress/runner/img/',
diff --git a/packages/runner-shared/.eslintrc.json b/packages/runner-shared/.eslintrc.json
new file mode 100644
index 0000000000..e9a3358d11
--- /dev/null
+++ b/packages/runner-shared/.eslintrc.json
@@ -0,0 +1,137 @@
+{
+ "plugins": [
+ "cypress",
+ "@cypress/dev"
+ ],
+ "extends": [
+ "plugin:@cypress/dev/general",
+ "plugin:@cypress/dev/tests",
+ "plugin:@cypress/dev/react",
+ "plugin:react/recommended",
+ "plugin:react-hooks/recommended",
+ "../reporter/src/.eslintrc.json"
+ ],
+ "parser": "@typescript-eslint/parser",
+ "env": {
+ "cypress/globals": true
+ },
+ "rules": {
+ "react/display-name": "off",
+ "react/function-component-definition": [
+ "error",
+ {
+ "namedComponents": "arrow-function",
+ "unnamedComponents": "arrow-function"
+ }
+ ],
+ "react/jsx-boolean-value": [
+ "error",
+ "always"
+ ],
+ "react/jsx-closing-bracket-location": [
+ "error",
+ "line-aligned"
+ ],
+ "react/jsx-closing-tag-location": "error",
+ "react/jsx-curly-brace-presence": [
+ "error",
+ {
+ "props": "never",
+ "children": "never"
+ }
+ ],
+ "react/jsx-curly-newline": "error",
+ "react/jsx-filename-extension": [
+ "warn",
+ {
+ "extensions": [
+ ".js",
+ ".jsx",
+ ".tsx"
+ ]
+ }
+ ],
+ "react/jsx-first-prop-new-line": "error",
+ "react/jsx-max-props-per-line": [
+ "error",
+ {
+ "maximum": 1,
+ "when": "multiline"
+ }
+ ],
+ "react/jsx-no-bind": [
+ "error",
+ {
+ "ignoreDOMComponents": true
+ }
+ ],
+ "react/jsx-no-useless-fragment": "error",
+ "react/jsx-one-expression-per-line": [
+ "error",
+ {
+ "allow": "literal"
+ }
+ ],
+ "react/jsx-sort-props": [
+ "error",
+ {
+ "callbacksLast": true,
+ "ignoreCase": true,
+ "noSortAlphabetically": true,
+ "reservedFirst": true
+ }
+ ],
+ "react/jsx-tag-spacing": [
+ "error",
+ {
+ "closingSlash": "never",
+ "beforeSelfClosing": "always"
+ }
+ ],
+ "react/jsx-wrap-multilines": [
+ "error",
+ {
+ "declaration": "parens-new-line",
+ "assignment": "parens-new-line",
+ "return": "parens-new-line",
+ "arrow": "parens-new-line",
+ "condition": "parens-new-line",
+ "logical": "parens-new-line",
+ "prop": "parens-new-line"
+ }
+ ],
+ "react/no-array-index-key": "error",
+ "react/no-unescaped-entities": "off",
+ "react/prop-types": "off",
+ "quote-props": [
+ "error",
+ "as-needed"
+ ]
+ },
+ "overrides": [
+ {
+ "files": [
+ "lib/*"
+ ],
+ "rules": {
+ "no-console": 1
+ }
+ },
+ {
+ "files": [
+ "**/*.json"
+ ],
+ "rules": {
+ "quotes": "off",
+ "comma-dangle": "off"
+ }
+ },
+ {
+ "files": "*.tsx",
+ "rules": {
+ "no-unused-vars": "off",
+ "react/jsx-no-bind": "off"
+ }
+ }
+ ]
+}
diff --git a/packages/runner-shared/package.json b/packages/runner-shared/package.json
new file mode 100644
index 0000000000..0df75b5d7d
--- /dev/null
+++ b/packages/runner-shared/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@packages/runner-shared",
+ "version": "0.0.0-development",
+ "private": true,
+ "main": "src/index.ts",
+ "scripts": {
+ "test": "yarn test-unit",
+ "test-unit": "mocha --config test/.mocharc.json src/**/*.spec.* --exit"
+ },
+ "dependencies": {
+ "@cypress/react-tooltip": "0.5.3",
+ "ansi-to-html": "0.6.14",
+ "classnames": "2.3.1",
+ "lodash": "4.17.21",
+ "mobx": "5.15.4",
+ "mobx-react": "6.1.8",
+ "react": "16.8.6",
+ "react-dom": "16.8.6"
+ },
+ "devDependencies": {
+ "@packages/driver": "0.0.0-development",
+ "@packages/socket": "0.0.0-development",
+ "@packages/web-config": "0.0.0-development",
+ "chai": "4.2.0",
+ "chai-enzyme": "1.0.0-beta.1",
+ "enzyme": "3.11.0",
+ "enzyme-adapter-react-16": "1.15.2",
+ "mocha": "7.0.1",
+ "sinon": "7.5.0",
+ "sinon-chai": "3.3.0"
+ }
+}
diff --git a/packages/runner/src/errors/automation-disconnected.spec.jsx b/packages/runner-shared/src/automation-disconnected/automation-disconnected.spec.jsx
similarity index 90%
rename from packages/runner/src/errors/automation-disconnected.spec.jsx
rename to packages/runner-shared/src/automation-disconnected/automation-disconnected.spec.jsx
index 68378bfd9f..567ef3338d 100644
--- a/packages/runner/src/errors/automation-disconnected.spec.jsx
+++ b/packages/runner-shared/src/automation-disconnected/automation-disconnected.spec.jsx
@@ -2,7 +2,7 @@ import React from 'react'
import { shallow } from 'enzyme'
import sinon from 'sinon'
-import AutomationDisconnected from './automation-disconnected'
+import { AutomationDisconnected } from '.'
describe('
', () => {
it('renders the message', () => {
diff --git a/packages/runner-ct/src/errors/automation-disconnected.jsx b/packages/runner-shared/src/automation-disconnected/index.jsx
similarity index 92%
rename from packages/runner-ct/src/errors/automation-disconnected.jsx
rename to packages/runner-shared/src/automation-disconnected/index.jsx
index 221ce25d84..0bb0131aae 100644
--- a/packages/runner-ct/src/errors/automation-disconnected.jsx
+++ b/packages/runner-shared/src/automation-disconnected/index.jsx
@@ -1,6 +1,6 @@
import React from 'react'
-export default ({ onReload }) => (
+export const AutomationDisconnected = ({ onReload }) => (
Whoops, the Cypress extension has disconnected.
diff --git a/packages/runner-shared/src/automation-element/index.tsx b/packages/runner-shared/src/automation-element/index.tsx
new file mode 100644
index 0000000000..dbbc84e292
--- /dev/null
+++ b/packages/runner-shared/src/automation-element/index.tsx
@@ -0,0 +1,17 @@
+import React from 'react'
+
+export const automationElementId = '__cypress-string'
+
+interface AutomationElementProps {
+ randomString: string
+}
+
+export const AutomationElement: React.FC
= ({
+ randomString,
+}) => {
+ return (
+
+ {randomString}
+
+ )
+}
diff --git a/packages/runner-ct/src/lib/automation.ts b/packages/runner-shared/src/automation.ts
similarity index 79%
rename from packages/runner-ct/src/lib/automation.ts
rename to packages/runner-shared/src/automation.ts
index 52302df3d4..239246f9e9 100644
--- a/packages/runner-ct/src/lib/automation.ts
+++ b/packages/runner-shared/src/automation.ts
@@ -1,4 +1,4 @@
-export default {
+export const automation = {
CONNECTING: 'CONNECTING',
MISSING: 'MISSING',
CONNECTED: 'CONNECTED',
diff --git a/packages/runner-ct/src/iframe/blank-contents.js b/packages/runner-shared/src/blank-contents/index.js
similarity index 98%
rename from packages/runner-ct/src/iframe/blank-contents.js
rename to packages/runner-shared/src/blank-contents/index.js
index 970e717fef..812ac6b79b 100644
--- a/packages/runner-ct/src/iframe/blank-contents.js
+++ b/packages/runner-shared/src/blank-contents/index.js
@@ -1,4 +1,4 @@
-export default () => {
+export const blankContents = () => {
return `
-
-
-
-
-
-
This is the default blank page.
-
To test your web application:
-
- Start your app's server
-
-
- cy.visit()
-
- your app
-
- Begin writing tests
-
-
- `
-}
diff --git a/packages/runner/src/iframe/iframes.jsx b/packages/runner/src/iframe/iframes.jsx
index e6a97bf74c..ab0a334c34 100644
--- a/packages/runner/src/iframe/iframes.jsx
+++ b/packages/runner/src/iframe/iframes.jsx
@@ -3,15 +3,16 @@ import { action, autorun } from 'mobx'
import { observer } from 'mobx-react'
import React, { Component } from 'react'
import { $ } from '@packages/driver'
+import {
+ SnapshotControls,
+ ScriptError,
+ IframeModel,
+ selectorPlaygroundModel,
+ AutIframe,
+ logger,
+ studioRecorder,
+} from '@packages/runner-shared'
-import AutIframe from './aut-iframe'
-import ScriptError from '../errors/script-error'
-import SnapshotControls from './snapshot-controls'
-
-import IframeModel from './iframe-model'
-import logger from '../lib/logger'
-import selectorPlaygroundModel from '../selector-playground/selector-playground-model'
-import studioRecorder from '../studio/studio-recorder'
import util from '../lib/util'
@observer
@@ -102,7 +103,6 @@ export default class Iframes extends Component {
this.iframeModel = new IframeModel({
state: this.props.state,
- removeHeadStyles: this.autIframe.removeHeadStyles,
restoreDom: this.autIframe.restoreDom,
highlightEl: this.autIframe.highlightEl,
detachDom: this.autIframe.detachDom,
@@ -122,7 +122,13 @@ export default class Iframes extends Component {
}
@action _setScriptError = (err) => {
- this.props.state.scriptError = err
+ if (err && 'error' in err) {
+ this.props.state.scriptError = err.error
+ }
+
+ if (!err) {
+ this.props.state.scriptError = null
+ }
}
_run = (config) => {
diff --git a/packages/runner/src/iframe/visit-failure.js b/packages/runner/src/iframe/visit-failure.js
deleted file mode 100644
index 896c105e61..0000000000
--- a/packages/runner/src/iframe/visit-failure.js
+++ /dev/null
@@ -1,86 +0,0 @@
-export default (props) => {
- const { status, statusText, contentType } = props
-
- const getContentType = () => {
- if (!contentType) {
- return ''
- }
-
- return `(${contentType})`
- }
-
- const getStatus = () => {
- if (!status) {
- return ''
- }
-
- return `${status} - ${statusText} ${getContentType()}
`
- }
-
- return `
-
-
-
-
-
-
-
Sorry, we could not load:
-
- ${props.url}
-
- ${getStatus()}
-
- `
-}
diff --git a/packages/runner/src/lib/automation.js b/packages/runner/src/lib/automation.js
deleted file mode 100644
index 52302df3d4..0000000000
--- a/packages/runner/src/lib/automation.js
+++ /dev/null
@@ -1,6 +0,0 @@
-export default {
- CONNECTING: 'CONNECTING',
- MISSING: 'MISSING',
- CONNECTED: 'CONNECTED',
- DISCONNECTED: 'DISCONNECTED',
-}
diff --git a/packages/runner/src/lib/config-file-formatted.jsx b/packages/runner/src/lib/config-file-formatted.jsx
deleted file mode 100644
index 4f6bccb6d9..0000000000
--- a/packages/runner/src/lib/config-file-formatted.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react'
-import { isUndefined } from 'lodash'
-
-const configFileFormatted = (configFile) => {
- if (configFile === false) {
- return <>cypress.json file (currently disabled by --config-file false)>
- }
-
- if (isUndefined(configFile) || configFile === 'cypress.json') {
- return <>cypress.json file>
- }
-
- return <>custom config file {configFile}>
-}
-
-export {
- configFileFormatted,
-}
diff --git a/packages/runner/src/lib/logger.js b/packages/runner/src/lib/logger.js
deleted file mode 100644
index a52ee1cacc..0000000000
--- a/packages/runner/src/lib/logger.js
+++ /dev/null
@@ -1,120 +0,0 @@
-/* eslint-disable no-console */
-
-import _ from 'lodash'
-
-export default {
- log (...args) {
- console.log(...args)
- },
-
- logError (...args) {
- console.error(...args)
- },
-
- clearLog () {
- if (console.clear) console.clear()
- },
-
- logFormatted (consoleProps) {
- if (_.isEmpty(consoleProps)) return
-
- this._logValues(consoleProps)
- this._logGroups(consoleProps)
- this._logTable(consoleProps)
- },
-
- _logValues (consoleProps) {
- const formattedLog = this._formatted(_.omit(consoleProps, 'groups', 'table'))
-
- _.each(formattedLog, (value, key) => {
- // don't log empty strings
- // _.trim([]) returns '' but we want to log empty arrays, so account for that
- if (_.trim(value) === '' && !_.isArray(value)) return
-
- this.log(`%c${key}`, 'font-weight: bold', value)
- })
- },
-
- _formatted (consoleProps) {
- const maxKeyLength = this._getMaxKeyLength(consoleProps)
-
- return _.reduce(consoleProps, (memo, value, key) => {
- const append = ': '
-
- key = _.chain(key + append).capitalize().padEnd(maxKeyLength + append.length, ' ').value()
- memo[key] = value
-
- return memo
- }, {})
- },
-
- _getMaxKeyLength (obj) {
- const lengths = _(obj).keys().map('length').value()
-
- return Math.max(...lengths)
- },
-
- _logGroups (consoleProps) {
- const groups = this._getGroups(consoleProps)
-
- _.each(groups, (group) => {
- console.groupCollapsed(group.name)
- _.each(group.items, (value, key) => {
- if (group.label === false) {
- this.log(value)
- } else {
- this.log(`%c${key}`, 'color: blue', value)
- }
- })
-
- console.groupEnd()
- })
- },
-
- _getGroups (consoleProps) {
- const groups = _.result(consoleProps, 'groups')
-
- if (!groups) return
-
- return _.map(groups, (group) => {
- group.items = this._formatted(group.items)
-
- return group
- })
- },
-
- _logTable (consoleProps) {
- if (isMultiEntryTable(consoleProps.table)) {
- _.each(
- _.sortBy(consoleProps.table, (val, key) => key),
- (table) => {
- return this._logTable({ table })
- },
- )
-
- return
- }
-
- const table = this._getTable(consoleProps)
-
- if (!table) return
-
- if (_.isArray(table)) {
- console.table(table)
- } else {
- console.groupCollapsed(table.name)
- console.table(table.data, table.columns)
- console.groupEnd()
- }
- },
-
- _getTable (consoleProps) {
- const table = _.result(consoleProps, 'table')
-
- if (!table) return
-
- return table
- },
-}
-
-const isMultiEntryTable = (table) => !_.isFunction(table) && !_.some(_.keys(table).map(isNaN).filter(Boolean), true)
diff --git a/packages/runner/src/lib/state.js b/packages/runner/src/lib/state.js
index 0d23a91081..46424b0321 100644
--- a/packages/runner/src/lib/state.js
+++ b/packages/runner/src/lib/state.js
@@ -1,5 +1,5 @@
import { action, computed, observable } from 'mobx'
-import automation from './automation'
+import { automation } from '@packages/runner-shared'
const _defaults = {
messageTitle: null,
diff --git a/packages/runner/src/main.jsx b/packages/runner/src/main.jsx
index 8cd6fbc627..24ceb65c82 100644
--- a/packages/runner/src/main.jsx
+++ b/packages/runner/src/main.jsx
@@ -3,8 +3,11 @@ import React from 'react'
import { render } from 'react-dom'
import { utils as driverUtils } from '@packages/driver'
+import App from './app/app'
+import NoSpec from './errors/no-spec'
import State from './lib/state'
-import Container from './app/container'
+import { Container, eventManager } from '@packages/runner-shared'
+import util from './lib/util'
configure({ enforceActions: 'always' })
@@ -22,7 +25,19 @@ const Runner = {
state.updateDimensions(config.viewportWidth, config.viewportHeight)
- render( , el)
+ const container = (
+
+ )
+
+ render(container, el)
})()
},
}
diff --git a/packages/runner/src/message/message.jsx b/packages/runner/src/message/message.jsx
deleted file mode 100644
index f317059956..0000000000
--- a/packages/runner/src/message/message.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import cs from 'classnames'
-import { observer } from 'mobx-react'
-import React, { forwardRef } from 'react'
-
-export default observer(forwardRef(({ state }, ref) => {
- if (!state.messageTitle) return null
-
- function controls () {
- if (!state.messageControls) return null
-
- return (
-
- {state.messageControls}
-
- )
- }
-
- return (
-
-
- {state.messageTitle}
- {state.messageDescription}
-
- {controls()}
-
- )
-}))
diff --git a/packages/runner/src/selector-playground/highlight.jsx b/packages/runner/src/selector-playground/highlight.jsx
deleted file mode 100644
index be4b10b44a..0000000000
--- a/packages/runner/src/selector-playground/highlight.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import _ from 'lodash'
-import React from 'react'
-import { render, unmountComponentAtNode } from 'react-dom'
-import Tooltip from '@cypress/react-tooltip'
-
-const Highlight = ({ selector, appendTo, styles, showTooltip = true }) => {
- return (
-
- {_.map(styles, (style, i) => {
- // indicates that tooltip should change if one of these props change
- const updateCue = _.values(_.pick(style, 'width', 'height', 'top', 'left', 'transform')).join()
-
- return (
-
-
-
- )
- })}
-
- )
-}
-
-function renderHighlight (container, props) {
- render( , container)
-}
-
-export default {
- render: renderHighlight,
- unmount: unmountComponentAtNode,
-}
diff --git a/packages/runner/src/selector-playground/selector-playground-model.js b/packages/runner/src/selector-playground/selector-playground-model.js
deleted file mode 100644
index 107d9e6ef6..0000000000
--- a/packages/runner/src/selector-playground/selector-playground-model.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import { action, computed, observable } from 'mobx'
-
-const methods = ['get', 'contains']
-
-class SelectorPlaygroundModel {
- methods = methods
-
- @observable getSelector = 'body'
- @observable containsSelector = 'Hello, World'
- @observable isOpen = false
- @observable isEnabled = false
- @observable isShowingHighlight = false
- @observable isValid = true
- @observable numElements = 0
- @observable method = methods[0]
-
- @computed get selector () {
- return this.method === 'get' ? this.getSelector : this.containsSelector
- }
-
- @computed get infoHelp () {
- if (!this.isValid) {
- return 'Invalid selector'
- }
-
- return this.numElements === 1 ? '1 matched element' : `${this.numElements} matched elements`
- }
-
- @action toggleEnabled () {
- this.setEnabled(!this.isEnabled)
- }
-
- @action setEnabled (isEnabled) {
- this.isEnabled = isEnabled
-
- if (!this.isEnabled) {
- this.isShowingHighlight = false
- }
- }
-
- @action toggleOpen () {
- this.setOpen(!this.isOpen)
- }
-
- @action setOpen (isOpen) {
- this.isOpen = isOpen
-
- this.setEnabled(this.isOpen)
- }
-
- @action setShowingHighlight (isShowingHighlight) {
- this.isShowingHighlight = isShowingHighlight
- }
-
- @action setSelector (selector) {
- if (this.method === 'get') {
- this.getSelector = selector
- } else {
- this.containsSelector = selector
- }
- }
-
- @action setNumElements (numElements) {
- this.numElements = numElements
- }
-
- @action setValidity (isValid) {
- this.isValid = isValid
- }
-
- @action setMethod (method) {
- this.method = method
- }
-
- @action resetMethod () {
- this.method = methods[0]
- }
-}
-
-export default new SelectorPlaygroundModel()
diff --git a/packages/runner/src/selector-playground/selector-playground.jsx b/packages/runner/src/selector-playground/selector-playground.jsx
deleted file mode 100644
index dae9fb1de8..0000000000
--- a/packages/runner/src/selector-playground/selector-playground.jsx
+++ /dev/null
@@ -1,237 +0,0 @@
-import _ from 'lodash'
-import cs from 'classnames'
-import { action, observable } from 'mobx'
-import { observer } from 'mobx-react'
-import React, { Component } from 'react'
-import Tooltip from '@cypress/react-tooltip'
-
-import eventManager from '../lib/event-manager'
-
-const defaultCopyText = 'Copy to clipboard'
-const defaultPrintText = 'Print to console'
-
-// mouseleave fires when entering a child element, so make sure we're
-// actually leaving the button and not just hovering over a child
-const fixMouseOut = (fn, getTarget) => (e) => {
- if (
- !e.relatedTarget
- || e.relatedTarget.parentNode === getTarget()
- || e.relatedTarget === getTarget()
- ) return
-
- fn(e)
-}
-
-@observer
-class SelectorPlayground extends Component {
- @observable copyText = defaultCopyText
- @observable printText = defaultPrintText
- @observable showingMethodPicker = false
-
- render () {
- const { model } = this.props
- const selectorText = `cy.${model.method}('${model.selector}')`
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- {this._methodSelector()}
-
(
-
{'\''}
-
- this._input = node}
- name={`${model.isEnabled}` /* fixes issue with not resizing when opening/closing selector playground */}
- value={model.selector}
- onChange={this._updateSelector}
- onFocus={this._setHighlight(true)}
- />
-
-
{'\''}
-
)
-
-
-
- {model.isValid ?
- model.numElements :
-
- }
-
-
-
-
- this._copyButton = node}
- className='copy-to-clipboard'
- onClick={this._copyToClipboard}
- disabled={!model.numElements || !model.isValid}
- onMouseOut={fixMouseOut(this._resetCopyText, () => this._copyButton)}
- >
-
-
-
-
- this._printButton = node}
- className='print-to-console'
- onClick={this._printToConsole}
- disabled={!model.numElements || !model.isValid}
- onMouseOut={fixMouseOut(this._resetPrintText, () => this._printButton)}
- >
-
-
-
-
-
- {' '}
- Learn more
-
-
x
-
- )
- }
-
- componentDidMount () {
- this._previousIsEnabled = this.props.model.isEnabled
- this._previousMethod = this.props.model.method
-
- document.body.addEventListener('click', this._onOutsideClick, false)
- }
-
- componentDidUpdate () {
- if (
- (this.props.model.isEnabled !== this._previousIsEnabled)
- || (this.props.model.method !== this._previousMethod)
- ) {
- if (this.props.model.isEnabled) {
- this._focusAndSelectInputText()
- }
-
- this._previousIsEnabled = this.props.model.isEnabled
- this._previousMethod = this.props.model.method
- }
- }
-
- componentWillUnmount () {
- document.body.removeEventListener('click', this._onOutsideClick)
- }
-
- _methodSelector () {
- const { model } = this.props
- const methods = _.filter(model.methods, (method) => method !== model.method)
-
- return (
-
-
- {' '}
- cy.{model.method}
-
-
- {_.map(methods, (method) => (
-
this._setMethod(method)}>
- cy.{method}
-
- ))}
-
-
- )
- }
-
- _focusAndSelectInputText () {
- this._input.focus()
- this._input.select()
- }
-
- _onOutsideClick = () => {
- this._setShowingMethodPicker(false)
- }
-
- _toggleMethodPicker = () => {
- this._setShowingMethodPicker(!this.showingMethodPicker)
- }
-
- @action _setShowingMethodPicker (isShowing) {
- this.showingMethodPicker = isShowing
- }
-
- @action _setMethod (method) {
- if (method !== this.props.model.method) {
- this.props.model.setMethod(method)
- }
- }
-
- _setHighlight = (isShowing) => () => {
- this.props.model.setShowingHighlight(isShowing)
- }
-
- _copyToClipboard = () => {
- try {
- this.refs.copyText.select()
- const successful = document.execCommand('copy')
-
- this._setCopyText(successful ? 'Copied!' : 'Oops, unable to copy')
- } catch (err) {
- this._setCopyText('Oops, unable to copy')
- }
- }
-
- @action _setCopyText (text) {
- this.copyText = text
- }
-
- _resetCopyText = () => {
- this._setCopyText(defaultCopyText)
- }
-
- _printToConsole = () => {
- eventManager.emit('print:selector:elements:to:console')
- this._setPrintText('Printed!')
- }
-
- @action _setPrintText (text) {
- this.printText = text
- }
-
- _resetPrintText = () => {
- this._setPrintText(defaultPrintText)
- }
-
- _toggleEnablingSelectorPlayground = () => {
- this.props.model.toggleEnabled()
- }
-
- _togglePlaygroundOpen = () => {
- this.props.model.toggleOpen()
- }
-
- _updateSelector = (e) => {
- const { model } = this.props
-
- model.setSelector(e.target.value)
- model.setShowingHighlight(true)
- }
-}
-
-export default SelectorPlayground
diff --git a/packages/web-config/node-jsdom-setup.ts b/packages/web-config/node-jsdom-setup.ts
index 7f20ef0721..59e1065d90 100644
--- a/packages/web-config/node-jsdom-setup.ts
+++ b/packages/web-config/node-jsdom-setup.ts
@@ -111,6 +111,10 @@ export const register = ({
return args[0]
}
+ if (args[0].endsWith('.scss')) {
+ return args[0]
+ }
+
const ret = _load.apply(this, browserPkg)
return ret
diff --git a/packages/web-config/webpack.config.base.ts b/packages/web-config/webpack.config.base.ts
index 51486df217..d202c3908a 100644
--- a/packages/web-config/webpack.config.base.ts
+++ b/packages/web-config/webpack.config.base.ts
@@ -55,7 +55,7 @@ const stats = {
timings: true,
}
-function makeSassLoaders ({ modules }): RuleSetRule {
+function makeSassLoaders ({ modules }: { modules: boolean }): RuleSetRule {
const exclude = [/node_modules/]
if (!modules) exclude.push(/\.modules?\.s[ac]ss$/i)