mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-22 06:59:30 -06:00
Merge branch 'develop' into feature-multidomain
This commit is contained in:
@@ -40,7 +40,7 @@ macWorkflowFilters: &mac-workflow-filters
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ feature-multidomain, << pipeline.git.branch >> ]
|
||||
- equal: [ use-contexts, << pipeline.git.branch >> ]
|
||||
- equal: [ new-cmd-group-9.x, << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
|
||||
1
cli/types/cypress.d.ts
vendored
1
cli/types/cypress.d.ts
vendored
@@ -5764,6 +5764,7 @@ declare namespace Cypress {
|
||||
name: string
|
||||
/** Override *name* for display purposes only */
|
||||
displayName: string
|
||||
/** additional information to include in the log */
|
||||
message: any
|
||||
/** Set to false if you want to control the finishing of the command in the log yourself */
|
||||
autoEnd: boolean
|
||||
|
||||
@@ -201,7 +201,7 @@ const startXhrServer = (cy, state, config) => {
|
||||
|
||||
Cypress.ProxyLogging.addXhrLog({ xhr, route, log, stack })
|
||||
|
||||
return log.snapshot('request')
|
||||
return log?.snapshot('request')
|
||||
},
|
||||
|
||||
onLoad: (xhr) => {
|
||||
|
||||
42
packages/driver/src/cy/logGroup.ts
Normal file
42
packages/driver/src/cy/logGroup.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { $Command } from '../cypress/command'
|
||||
import $errUtils from '../cypress/error_utils'
|
||||
|
||||
export default (Cypress, userOptions: Cypress.LogGroup.Config, fn: Cypress.LogGroup.ApiCallback) => {
|
||||
const cy = Cypress.cy
|
||||
|
||||
const shouldEmitLog = userOptions.log === undefined ? true : userOptions.log
|
||||
|
||||
const options: Cypress.InternalLogConfig = {
|
||||
...userOptions,
|
||||
instrument: 'command',
|
||||
groupStart: true,
|
||||
emitOnly: !shouldEmitLog,
|
||||
}
|
||||
|
||||
const log = Cypress.log(options)
|
||||
|
||||
if (!_.isFunction(fn)) {
|
||||
$errUtils.throwErrByPath('group.missing_fn', { onFail: log })
|
||||
}
|
||||
|
||||
// An internal command is inserted to create a divider between
|
||||
// commands inside group() callback and commands chained to it.
|
||||
const restoreCmdIndex = cy.state('index') + 1
|
||||
|
||||
const endLogGroupCmd = $Command.create({
|
||||
name: 'end-logGroup',
|
||||
injected: true,
|
||||
})
|
||||
|
||||
const forwardYieldedSubject = () => {
|
||||
if (log) {
|
||||
log.endGroup()
|
||||
}
|
||||
|
||||
return endLogGroupCmd.get('prev').get('subject')
|
||||
}
|
||||
|
||||
cy.queue.insert(restoreCmdIndex, endLogGroupCmd.set('fn', forwardYieldedSubject))
|
||||
|
||||
return fn(log)
|
||||
}
|
||||
@@ -128,7 +128,7 @@ export function addCommands (Commands, Cypress: Cypress.Cypress, cy: Cypress.cy,
|
||||
|
||||
const _reject = (err) => {
|
||||
cleanup()
|
||||
log.error(err)
|
||||
log?.error(err)
|
||||
reject(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ export function getDisplayUrlMatcher (matcher: RouteMatcherOptions): string {
|
||||
return $utils.stringify(displayMatcher)
|
||||
}
|
||||
|
||||
export function getRouteMatcherLogConfig (matcher: RouteMatcherOptions, isStubbed: boolean, alias: string | void, staticResponse?: StaticResponse): Partial<Cypress.LogConfig> {
|
||||
const obj: Partial<Cypress.LogConfig> = {
|
||||
export function getRouteMatcherLogConfig (matcher: RouteMatcherOptions, isStubbed: boolean, alias: string | void, staticResponse?: StaticResponse): Partial<Cypress.InternalLogConfig> {
|
||||
const obj: Partial<Cypress.InternalLogConfig> = {
|
||||
name: 'route',
|
||||
method: String(matcher.method || '*'),
|
||||
url: getDisplayUrlMatcher(matcher),
|
||||
|
||||
@@ -583,7 +583,9 @@ export default {
|
||||
docsUrl: 'https://on.cypress.io/go',
|
||||
},
|
||||
},
|
||||
|
||||
group: {
|
||||
missing_fn: '`group` API must be called with a function.',
|
||||
},
|
||||
hover: {
|
||||
not_implemented: {
|
||||
message: [
|
||||
|
||||
@@ -112,7 +112,7 @@ export const LogUtils = {
|
||||
},
|
||||
}
|
||||
|
||||
const defaults = function (state, config, obj) {
|
||||
const defaults = function (state: Cypress.State, config, obj) {
|
||||
const instrument = obj.instrument != null ? obj.instrument : 'command'
|
||||
|
||||
// dont set any defaults if this
|
||||
@@ -129,7 +129,7 @@ const defaults = function (state, config, obj) {
|
||||
// but in cases where the command purposely does not log
|
||||
// then it could still be logged during a failure, which
|
||||
// is why we normalize its type value
|
||||
if (!parentOrChildRe.test(obj.type)) {
|
||||
if (typeof obj.type === 'string' && !parentOrChildRe.test(obj.type)) {
|
||||
// does this command have a previously linked command
|
||||
// by chainer id
|
||||
obj.type = (current != null ? current.hasPreviouslyLinkedCommand() : undefined) ? 'child' : 'parent'
|
||||
@@ -206,18 +206,18 @@ const defaults = function (state, config, obj) {
|
||||
},
|
||||
})
|
||||
|
||||
const logGroup = _.last(state('logGroup'))
|
||||
const logGroupIds = state('logGroupIds') || []
|
||||
|
||||
if (logGroup) {
|
||||
obj.group = logGroup
|
||||
if (logGroupIds.length) {
|
||||
obj.group = _.last(logGroupIds)
|
||||
}
|
||||
|
||||
if (obj.groupEnd) {
|
||||
state('logGroup', _.slice(state('logGroup'), 0, -1))
|
||||
state('logGroupIds', _.slice(logGroupIds, 0, -1))
|
||||
}
|
||||
|
||||
if (obj.groupStart) {
|
||||
state('logGroup', (state('logGroup') || []).concat(obj.id))
|
||||
state('logGroupIds', (logGroupIds).concat(obj.id))
|
||||
}
|
||||
|
||||
return obj
|
||||
@@ -225,7 +225,7 @@ const defaults = function (state, config, obj) {
|
||||
|
||||
class Log {
|
||||
cy: any
|
||||
state: any
|
||||
state: Cypress.State
|
||||
config: any
|
||||
fireChangeEvent: ((log) => (void | undefined))
|
||||
obj: any
|
||||
@@ -236,7 +236,8 @@ class Log {
|
||||
this.cy = cy
|
||||
this.state = state
|
||||
this.config = config
|
||||
this.fireChangeEvent = fireChangeEvent
|
||||
// only fire the log:state:changed event as fast as every 4ms
|
||||
this.fireChangeEvent = _.debounce(fireChangeEvent, 4)
|
||||
this.obj = defaults(state, config, obj)
|
||||
|
||||
extendEvents(this)
|
||||
@@ -376,6 +377,13 @@ class Log {
|
||||
}
|
||||
|
||||
error (err) {
|
||||
const logGroupIds = this.state('logGroupIds') || []
|
||||
|
||||
// current log was responsible to creating the current log group so end the current group
|
||||
if (_.last(logGroupIds) === this.attributes.id) {
|
||||
this.endGroup()
|
||||
}
|
||||
|
||||
this.set({
|
||||
ended: true,
|
||||
error: err,
|
||||
@@ -404,6 +412,10 @@ class Log {
|
||||
return this
|
||||
}
|
||||
|
||||
endGroup () {
|
||||
this.state('logGroupIds', _.slice(this.state('logGroupIds'), 0, -1))
|
||||
}
|
||||
|
||||
getError (err) {
|
||||
return err.stack || err.message
|
||||
}
|
||||
@@ -561,16 +573,8 @@ class LogManager {
|
||||
this.logs[id] = true
|
||||
}
|
||||
|
||||
// only fire the log:state:changed event
|
||||
// as fast as every 4ms
|
||||
fireChangeEvent (log) {
|
||||
const triggerStateChanged = () => {
|
||||
return this.trigger(log, 'command:log:changed')
|
||||
}
|
||||
|
||||
const debounceFn = _.debounce(triggerStateChanged, 4)
|
||||
|
||||
return debounceFn()
|
||||
return this.trigger(log, 'command:log:changed')
|
||||
}
|
||||
|
||||
createLogFn (cy, state, config) {
|
||||
|
||||
@@ -50,7 +50,7 @@ function getDisplayUrl (url: string) {
|
||||
return url
|
||||
}
|
||||
|
||||
function getDynamicRequestLogConfig (req: Omit<ProxyRequest, 'log'>): Partial<Cypress.LogConfig> {
|
||||
function getDynamicRequestLogConfig (req: Omit<ProxyRequest, 'log'>): Partial<Cypress.InternalLogConfig> {
|
||||
const last = _.last(req.interceptions)
|
||||
let alias = last ? last.interception.request.alias || last.route.alias : undefined
|
||||
|
||||
@@ -64,7 +64,7 @@ function getDynamicRequestLogConfig (req: Omit<ProxyRequest, 'log'>): Partial<Cy
|
||||
}
|
||||
}
|
||||
|
||||
function getRequestLogConfig (req: Omit<ProxyRequest, 'log'>): Partial<Cypress.LogConfig> {
|
||||
function getRequestLogConfig (req: Omit<ProxyRequest, 'log'>): Partial<Cypress.InternalLogConfig> {
|
||||
function getStatus (): string | undefined {
|
||||
const { stubbed, reqModified, resModified } = req.flags
|
||||
|
||||
@@ -392,7 +392,7 @@ export default class ProxyLogging {
|
||||
const proxyRequest = new ProxyRequest(preRequest)
|
||||
const logConfig = getRequestLogConfig(proxyRequest as Omit<ProxyRequest, 'log'>)
|
||||
|
||||
proxyRequest.log = this.Cypress.log(logConfig).snapshot('request')
|
||||
proxyRequest.log = this.Cypress.log(logConfig)?.snapshot('request')
|
||||
|
||||
this.proxyRequests.push(proxyRequest as ProxyRequest)
|
||||
|
||||
|
||||
@@ -1774,10 +1774,9 @@ export default {
|
||||
test = getTestById(testId)
|
||||
|
||||
if (test) {
|
||||
// pluralize the instrument
|
||||
// as a property on the runnable
|
||||
let name
|
||||
const logs = test[name = `${instrument}s`] != null ? test[name] : (test[name] = [])
|
||||
// pluralize the instrument as a property on the runnable
|
||||
const name = `${instrument}s`
|
||||
const logs = test[name] != null ? test[name] : (test[name] = [])
|
||||
|
||||
// else push it onto the logs
|
||||
return logs.push(attrs)
|
||||
|
||||
27
packages/driver/types/cy/logGroup.d.ts
vendored
Normal file
27
packages/driver/types/cy/logGroup.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// The type declarations for Cypress Log Group & the corresponding configuration permutations
|
||||
declare namespace Cypress {
|
||||
declare namespace LogGroup {
|
||||
type ApiCallback = (log: Cypress.Log) => Chainable<S>
|
||||
type LogGroup = (cypress: Cypress.Cypress, options: Partial<LogGroupConfig>, callback: LogGroupCallback) => Chainable<S>
|
||||
|
||||
interface Config {
|
||||
// the JQuery element for the command. This will highlight the command
|
||||
// in the main window when debugging
|
||||
$el?: JQuery
|
||||
// whether or not to emit a log to the UI
|
||||
// when disabled, child logs will not be nested in the UI
|
||||
log?: boolean
|
||||
// name of the group - defaults to current command's name
|
||||
name?: string
|
||||
// additional information to include in the log
|
||||
message?: string
|
||||
// timeout of the group command - defaults to defaultCommandTimeout
|
||||
timeout?: number
|
||||
// the type of log
|
||||
// system - log generated by Cypress
|
||||
// parent - log generated by Command
|
||||
// child - log generated by Chained Command
|
||||
type?: Cypress.InternalLogConfig['type']
|
||||
}
|
||||
}
|
||||
}
|
||||
119
packages/driver/types/cypress/log.d.ts
vendored
Normal file
119
packages/driver/types/cypress/log.d.ts
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
// The type declarations for Cypress Logs & the corresponding configuration permutations
|
||||
declare namespace Cypress {
|
||||
interface Cypress {
|
||||
log(options: Partial<LogConfig | InternalLogConfig>): Log | undefined
|
||||
}
|
||||
|
||||
interface Log extends Log {
|
||||
set<K extends keyof LogConfig | InternalLogConfig>(key: K, value: LogConfig[K]): InternalLog
|
||||
set(options: Partial<LogConfig | InternalLogConfig>)
|
||||
groupEnd(): void
|
||||
}
|
||||
|
||||
type ReferenceAlias = {
|
||||
cardinal: number,
|
||||
name: string,
|
||||
ordinal: string,
|
||||
}
|
||||
|
||||
type Snapshot = {
|
||||
body?: {get: () => any},
|
||||
htmlAttrs?: {[key: string]: any},
|
||||
name?: string
|
||||
}
|
||||
|
||||
type ConsoleProps = {
|
||||
Command?: string
|
||||
Snapshot?: string
|
||||
Elements?: number
|
||||
Selector?: string
|
||||
Yielded?: HTMLElement
|
||||
Event?: string
|
||||
Message?: string
|
||||
actual?: any
|
||||
expected?: any
|
||||
}
|
||||
|
||||
type RenderProps = {
|
||||
indicator?: 'aborted' | 'pending' | 'successful' | 'bad'
|
||||
message?: string
|
||||
}
|
||||
|
||||
interface InternalLogConfig {
|
||||
// the JQuery element for the command. This will highlight the command
|
||||
// in the main window when debugging
|
||||
$el?: JQuery | string
|
||||
alias?: string
|
||||
aliasType?: 'agent' | 'route' | 'primitive' | 'dom' | undefined
|
||||
browserPreRequest?: any
|
||||
callCount?: number
|
||||
chainerId?: string
|
||||
commandName?: string
|
||||
// provide the content to display in the dev tool's console when a log is
|
||||
// clicked from the Reporter's Command Log
|
||||
consoleProps?: () => Command | Command
|
||||
coords?: {
|
||||
left: number
|
||||
leftCenter: number
|
||||
top: number
|
||||
topCenter: number
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
count?: number
|
||||
// the name override for display purposes only
|
||||
displayName?: string
|
||||
// whether or not to show the log in the Reporter UI or only
|
||||
// store the log details on the command and log manager
|
||||
emitOnly?: boolean
|
||||
end?: boolean
|
||||
ended?: boolean
|
||||
err?: Error
|
||||
error?: Error
|
||||
// whether or not the generated log was an event or command
|
||||
event?: boolean
|
||||
expected?: string
|
||||
functionName?: string
|
||||
// whether or not to start a new log group
|
||||
groupStart?: boolean
|
||||
hookId?: number
|
||||
id?: string
|
||||
// defaults to command
|
||||
instrument?: 'agent' | 'command' | 'route'
|
||||
// whether or not the xhr route had a corresponding response stubbed out
|
||||
isStubbed?: boolean
|
||||
// additional information to include in the log if not overridden
|
||||
// the render props message
|
||||
// defaults to command arguments for command instrument
|
||||
message?: string | Array<string> | any[]
|
||||
method?: string
|
||||
// name of the log
|
||||
name?: string
|
||||
numElements?: number
|
||||
// the number of xhr responses that occurred. This is only applicable to
|
||||
// logs defined with instrument=route
|
||||
numResponses?: number
|
||||
referencesAlias?: ReferenceAlias[]
|
||||
renderProps?: () => RenderProps | RenderProps
|
||||
response?: string | object
|
||||
selector?: any
|
||||
snapshot?: boolean
|
||||
snapshots?: []
|
||||
state?: "failed" | "passed" | "pending" // representative of Mocha.Runnable.constants (not publicly exposed by Mocha types)
|
||||
status?: number
|
||||
testCurrentRetry?: number
|
||||
testId?: string
|
||||
// timeout of the group command - defaults to defaultCommandTimeout
|
||||
timeout?: number
|
||||
// the type of log
|
||||
// system - log generated by Cypress
|
||||
// parent - log generated by Command
|
||||
// child - log generated by Chained Command
|
||||
type?: 'system' | 'parent' | 'child' | ((current: State['state']['current'], subject: State['state']['subject']) => 'parent' | 'child')
|
||||
url?: string
|
||||
viewportHeight?: number
|
||||
viewportWidth?: number
|
||||
visible?: boolean
|
||||
wallClockStartedAt?: string
|
||||
}
|
||||
}
|
||||
90
packages/driver/types/internal-types.d.ts
vendored
90
packages/driver/types/internal-types.d.ts
vendored
@@ -1,5 +1,7 @@
|
||||
// NOTE: this is for internal Cypress types that we don't want exposed in the public API but want for development
|
||||
// TODO: find a better place for this
|
||||
/// <reference path="./cy/logGroup.d.ts" />
|
||||
/// <reference path="./cypress/log.d.ts" />
|
||||
|
||||
interface InternalWindowLoadDetails {
|
||||
type: 'same:domain' | 'cross:domain' | 'cross:domain:failure'
|
||||
@@ -73,97 +75,14 @@ declare namespace Cypress {
|
||||
warning: (message: string) => void
|
||||
}
|
||||
|
||||
type Log = ReturnType<Cypress.log>
|
||||
|
||||
type ReferenceAlias = {
|
||||
cardinal: number,
|
||||
name: string,
|
||||
ordinal: string,
|
||||
}
|
||||
|
||||
type Snapshot = {
|
||||
body?: {get: () => any},
|
||||
htmlAttrs?: {[key: string]: any},
|
||||
name?: string
|
||||
}
|
||||
|
||||
type ConsoleProps = {
|
||||
Command?: string
|
||||
Snapshot?: string
|
||||
Elements?: number
|
||||
Selector?: string
|
||||
Yielded?: HTMLElement
|
||||
Event?: string
|
||||
Message?: string
|
||||
actual?: any
|
||||
expected?: any
|
||||
}
|
||||
|
||||
type RenderProps = {
|
||||
indicator?: 'aborted' | 'pending' | 'successful' | 'bad'
|
||||
message?: string
|
||||
}
|
||||
|
||||
interface LogConfig {
|
||||
$el?: jQuery<any> | string
|
||||
message: any
|
||||
instrument?: 'route' | 'command'
|
||||
isStubbed?: boolean
|
||||
alias?: string
|
||||
aliasType?: 'route'
|
||||
referencesAlias?: ReferenceAlias[]
|
||||
chainerId?: string
|
||||
commandName?: string
|
||||
coords?: {
|
||||
left: number
|
||||
leftCenter: number
|
||||
top: number
|
||||
topCenter: number
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
count?: number
|
||||
callCount?: number
|
||||
type?: 'parent' | 'child'
|
||||
event?: boolean
|
||||
end?: boolean
|
||||
ended?: boolean
|
||||
expected?: string
|
||||
functionName?: string
|
||||
name?: string
|
||||
id?: string
|
||||
hookId?: number
|
||||
method?: string
|
||||
url?: string
|
||||
status?: number
|
||||
state?: "failed" | "passed" | "pending" // representative of Mocha.Runnable.constants (not publicly exposed by Mocha types)
|
||||
numResponses?: number
|
||||
numElements?: number
|
||||
numResponses?: number
|
||||
response?: string | object
|
||||
testCurrentRetry?: number
|
||||
timeout?: number
|
||||
testId?: string
|
||||
err?: Error
|
||||
error?: Error
|
||||
snapshot?: boolean
|
||||
snapshots?: []
|
||||
selector?: any
|
||||
viewportHeight?: number
|
||||
viewportWidth?: number
|
||||
visible?: boolean
|
||||
wallClockStartedAt?: string
|
||||
renderProps?: () => RenderProps | RenderProps
|
||||
consoleProps?: () => Command | Command
|
||||
browserPreRequest?: any
|
||||
}
|
||||
|
||||
// Extend Cypress.state properties here
|
||||
interface State {
|
||||
(k: '$autIframe', v?: JQuery<HTMLIFrameElement>): JQuery<HTMLIFrameElement> | undefined
|
||||
(k: 'routes', v?: RouteMap): RouteMap
|
||||
(k: 'aliasedRequests', v?: AliasedRequest[]): AliasedRequest[]
|
||||
(k: 'document', v?: Document): Document
|
||||
(k: 'window', v?: Window): Window
|
||||
(k: 'logGroupIds', v?: Array<InternalLogConfig['id']>): Array<InternalLogConfig['id']>
|
||||
(k: string, v?: any): any
|
||||
state: Cypress.state
|
||||
}
|
||||
@@ -172,7 +91,6 @@ declare namespace Cypress {
|
||||
(k: keyof ResolvedConfigOptions, v?: any): any
|
||||
}
|
||||
|
||||
// Extend Cypress.state properties here
|
||||
interface ResolvedConfigOptions {
|
||||
$autIframe: JQuery<HTMLIFrameElement>
|
||||
document: Document
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"projectId": "ypt4pf",
|
||||
"baseUrl": "http://localhost:5006",
|
||||
"viewportWidth": 400,
|
||||
"viewportHeight": 450,
|
||||
"viewportHeight": 1000,
|
||||
"reporter": "../../node_modules/cypress-multi-reporters/index.js",
|
||||
"reporterOptions": {
|
||||
"configFile": "../../mocha-reporter-config.json"
|
||||
|
||||
@@ -154,6 +154,18 @@
|
||||
"testId": "r3",
|
||||
"timeout": 4000,
|
||||
"type": "parent"
|
||||
},
|
||||
{
|
||||
"hookId": "r3",
|
||||
"id": 240,
|
||||
"instrument": "command",
|
||||
"message": "System Event Command",
|
||||
"name": "cmd",
|
||||
"state": "passed",
|
||||
"event": true,
|
||||
"testId": "r3",
|
||||
"timeout": 4000,
|
||||
"type": "system"
|
||||
}
|
||||
],
|
||||
"invocationDetails": {
|
||||
|
||||
@@ -281,7 +281,7 @@ describe('aliases', () => {
|
||||
cy.get('.command-wrapper')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('.num-children').should('not.be.visible')
|
||||
cy.get('.num-children').should('not.exist')
|
||||
|
||||
cy.contains('.command-interceptions', 'getPosts')
|
||||
})
|
||||
@@ -535,7 +535,7 @@ describe('aliases', () => {
|
||||
cy.get('.command-wrapper')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('.num-children').should('not.be.visible')
|
||||
cy.get('.num-children').should('not.exist')
|
||||
|
||||
cy.contains('.command-alias', 'dropdown')
|
||||
})
|
||||
|
||||
@@ -42,7 +42,143 @@ describe('commands', () => {
|
||||
})
|
||||
|
||||
it('displays all the commands', () => {
|
||||
cy.get('.command').should('have.length', 9)
|
||||
addCommand(runner, {
|
||||
id: 102,
|
||||
name: 'get',
|
||||
message: '#element',
|
||||
state: 'passed',
|
||||
timeout: 4000,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 124,
|
||||
name: 'within',
|
||||
state: 'passed',
|
||||
type: 'child',
|
||||
timeout: 4000,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 125,
|
||||
name: 'get',
|
||||
message: '#my_element',
|
||||
state: 'passed',
|
||||
timeout: 4000,
|
||||
group: 124,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 129,
|
||||
name: 'within',
|
||||
state: 'passed',
|
||||
type: 'child',
|
||||
group: 124,
|
||||
timeout: 4000,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 130,
|
||||
name: 'get',
|
||||
message: '#my_element that _has_ a really long message to show **wrapping** works as expected',
|
||||
state: 'passed',
|
||||
timeout: 4000,
|
||||
groupLevel: 1,
|
||||
group: 129,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 1229,
|
||||
name: 'within',
|
||||
state: 'passed',
|
||||
type: 'child',
|
||||
group: 130,
|
||||
groupLevel: 1,
|
||||
timeout: 4000,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 1311,
|
||||
name: 'get',
|
||||
message: '#my_element_nested',
|
||||
state: 'passed',
|
||||
timeout: 4000,
|
||||
groupLevel: 2,
|
||||
group: 1229,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 1291,
|
||||
name: 'assert',
|
||||
type: 'child',
|
||||
message: 'has class named .omg',
|
||||
state: 'passed',
|
||||
timeout: 4000,
|
||||
group: 1229,
|
||||
groupLevel: 2,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 1291,
|
||||
name: 'log',
|
||||
message: 'do something else',
|
||||
state: 'passed',
|
||||
timeout: 4000,
|
||||
group: 130,
|
||||
groupLevel: 1,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
id: 135,
|
||||
name: 'and',
|
||||
type: 'child',
|
||||
message: 'has class named .lets-roll',
|
||||
state: 'passed',
|
||||
timeout: 4000,
|
||||
group: 124,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
|
||||
const indicators = ['successful', 'pending', 'aborted', 'bad']
|
||||
|
||||
indicators.forEach((indicator, index) => {
|
||||
addCommand(runner, {
|
||||
id: 1600 + index,
|
||||
name: 'xhr',
|
||||
event: true,
|
||||
state: 'passed',
|
||||
timeout: 4000,
|
||||
renderProps: {
|
||||
indicator,
|
||||
message: `${indicator} indicator`,
|
||||
},
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
})
|
||||
|
||||
const assertStates = ['passed', 'pending', 'failed']
|
||||
|
||||
assertStates.forEach((state, index) => {
|
||||
addCommand(runner, {
|
||||
id: 1700 + index,
|
||||
name: 'assert',
|
||||
type: 'child',
|
||||
message: 'expected **element** to have length of **16** but got **12** instead',
|
||||
state,
|
||||
timeout: 4000,
|
||||
wallClockStartedAt: inProgressStartedAt,
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('.command').should('have.length', 27)
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
@@ -61,6 +197,12 @@ describe('commands', () => {
|
||||
})
|
||||
|
||||
it('includes the state class', () => {
|
||||
addCommand(runner, {
|
||||
name: 'log',
|
||||
message: 'command-warning-state',
|
||||
state: 'warning',
|
||||
})
|
||||
|
||||
cy.contains('#exists').closest('.command')
|
||||
.should('have.class', 'command-state-passed')
|
||||
|
||||
@@ -69,6 +211,9 @@ describe('commands', () => {
|
||||
|
||||
cy.contains('#in-progress').closest('.command')
|
||||
.should('have.class', 'command-state-pending')
|
||||
|
||||
cy.contains('command-warning-state').closest('.command')
|
||||
.should('have.class', 'command-state-warning')
|
||||
})
|
||||
|
||||
it('displays the number', () => {
|
||||
@@ -114,13 +259,42 @@ describe('commands', () => {
|
||||
})
|
||||
|
||||
it('shows indicator when specified', () => {
|
||||
cy.contains('GET ---').closest('.command').find('.command-message .fa-circle')
|
||||
.should('be.visible')
|
||||
const indicators = ['successful', 'pending', 'aborted', 'bad']
|
||||
|
||||
indicators.forEach((indicator) => {
|
||||
addCommand(runner, {
|
||||
name: 'xhr',
|
||||
event: true,
|
||||
renderProps: {
|
||||
indicator,
|
||||
message: `${indicator} indicator`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
indicators.forEach((indicator) => {
|
||||
cy.contains(`${indicator} indicator`).closest('.command').find('.command-message .fa-circle')
|
||||
.should('be.visible')
|
||||
})
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
|
||||
it('includes the renderProps indicator as a class name when specified', () => {
|
||||
cy.contains('Lorem ipsum').closest('.command').find('.command-message .fa-circle')
|
||||
.should('have.class', 'bad')
|
||||
it('assert commands for each state', () => {
|
||||
const assertStates = ['passed', 'pending', 'failed']
|
||||
|
||||
assertStates.forEach((state) => {
|
||||
addCommand(runner, {
|
||||
name: 'assert',
|
||||
type: 'child',
|
||||
message: `expected **element** to have **state of ${state}**`,
|
||||
state,
|
||||
})
|
||||
})
|
||||
|
||||
cy.get('.command').should('have.length', 13)
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
|
||||
describe('progress bar', () => {
|
||||
@@ -218,14 +392,16 @@ describe('commands', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('duplicates', () => {
|
||||
it('collapses consecutive duplicate events into one', () => {
|
||||
context('event duplicates', () => {
|
||||
it('collapses consecutive duplicate events into group', () => {
|
||||
cy.get('.command-name-xhr').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('displays number of duplicates', () => {
|
||||
cy.contains('GET --- /dup').closest('.command').find('.num-children')
|
||||
.should('have.text', '4')
|
||||
.trigger('mouseover')
|
||||
.get('.cy-tooltip').should('have.text', 'This event occurred 4 times')
|
||||
})
|
||||
|
||||
it('expands all events after clicking arrow', () => {
|
||||
@@ -328,6 +504,133 @@ describe('commands', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('command group', () => {
|
||||
let groupId
|
||||
|
||||
beforeEach(() => {
|
||||
groupId = addCommand(runner, {
|
||||
name: 'group',
|
||||
message: 'example group command',
|
||||
type: 'parent',
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
name: 'get',
|
||||
message: '#my_nested_element',
|
||||
group: groupId,
|
||||
})
|
||||
})
|
||||
|
||||
it('group is open by default when all nested command have passed', () => {
|
||||
addCommand(runner, {
|
||||
name: 'log',
|
||||
message: 'chained log example',
|
||||
})
|
||||
|
||||
cy.contains('chained log example') // ensure test content has loaded
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.should('have.class', 'command-is-open')
|
||||
.find('.command-expander')
|
||||
.should('be.visible')
|
||||
.closest('.command-name-group')
|
||||
.click()
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.should('not.have.class', 'command-is-open')
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.find('.num-children')
|
||||
.should('have.text', '1')
|
||||
.trigger('mouseover')
|
||||
.get('.cy-tooltip').should('have.text', '1 log currently hidden')
|
||||
.percySnapshot()
|
||||
})
|
||||
|
||||
it('group is open by default when last nested command failed', () => {
|
||||
addCommand(runner, {
|
||||
name: 'log',
|
||||
message: 'chained log example',
|
||||
state: 'failed',
|
||||
group: groupId,
|
||||
})
|
||||
|
||||
cy.contains('chained log example') // ensure test content has loaded
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.should('have.class', 'command-is-open')
|
||||
.find('.command-expander')
|
||||
.should('be.visible')
|
||||
.closest('.command-name-group')
|
||||
.click()
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.find('.num-children')
|
||||
.should('not.exist')
|
||||
.percySnapshot()
|
||||
})
|
||||
|
||||
it('clicking opens and closes the group', () => {
|
||||
cy.get('.command-name-group')
|
||||
.find('.num-children')
|
||||
.should('not.exist')
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.should('have.class', 'command-is-open')
|
||||
.find('.command-expander')
|
||||
.should('be.visible')
|
||||
.closest('.command-name-group')
|
||||
.click()
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.find('.num-children')
|
||||
.should('be.visible')
|
||||
.should('have.text', '1')
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.should('not.have.class', 'command-is-open')
|
||||
})
|
||||
|
||||
it('displays with nested logs', () => {
|
||||
const nestedGroupId = addCommand(runner, {
|
||||
name: 'group-2',
|
||||
state: 'passed',
|
||||
type: 'child',
|
||||
group: groupId,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
name: 'get',
|
||||
message: '#my_element_nested',
|
||||
state: 'passed',
|
||||
group: nestedGroupId,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
name: 'assert',
|
||||
type: 'child',
|
||||
message: 'has class named .omg',
|
||||
group: nestedGroupId,
|
||||
})
|
||||
|
||||
addCommand(runner, {
|
||||
name: 'log',
|
||||
message: 'chained log example',
|
||||
state: 'passed',
|
||||
group: groupId,
|
||||
})
|
||||
|
||||
cy.get('.command-name-group')
|
||||
.should('have.class', 'command-is-open')
|
||||
|
||||
cy.get('.command-name-group-2')
|
||||
.should('have.class', 'command-is-open')
|
||||
.click()
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
context('studio commands', () => {
|
||||
beforeEach(() => {
|
||||
addCommand(runner, {
|
||||
|
||||
@@ -27,6 +27,110 @@ describe('Command model', () => {
|
||||
clock.restore()
|
||||
})
|
||||
|
||||
context('.visible', () => {
|
||||
let command: CommandModel
|
||||
|
||||
it('sets visible to true for command has visible elements associated to it', () => {
|
||||
command = new CommandModel(commandProps({ visible: true }))
|
||||
expect(command.visible).to.be.true
|
||||
})
|
||||
|
||||
it('sets visible to false for command has hidden elements associated to it', () => {
|
||||
command = new CommandModel(commandProps({ visible: false }))
|
||||
expect(command.visible).to.be.false
|
||||
})
|
||||
|
||||
it('sets visible to true for command that does not associate with visibility', () => {
|
||||
command = new CommandModel(commandProps({ visible: undefined }))
|
||||
expect(command.visible).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
context('.numChildren', () => {
|
||||
context('event log', () => {
|
||||
it('with no children', () => {
|
||||
const command = new CommandModel(commandProps({ event: true }))
|
||||
|
||||
expect(command.numChildren).to.eq(1)
|
||||
})
|
||||
|
||||
it('with children', () => {
|
||||
const command = new CommandModel(commandProps({ event: true }))
|
||||
|
||||
command.addChild(new CommandModel(commandProps()))
|
||||
expect(command.numChildren).to.eq(2)
|
||||
|
||||
command.addChild(new CommandModel(commandProps()))
|
||||
expect(command.numChildren).to.eq(3)
|
||||
})
|
||||
})
|
||||
|
||||
context('command log', () => {
|
||||
it('with no children', () => {
|
||||
const command = new CommandModel(commandProps({}))
|
||||
|
||||
expect(command.numChildren).to.eq(0)
|
||||
})
|
||||
|
||||
it('with children', () => {
|
||||
const command = new CommandModel(commandProps({}))
|
||||
|
||||
command.addChild(new CommandModel(commandProps()))
|
||||
expect(command.numChildren).to.eq(1)
|
||||
|
||||
command.addChild(new CommandModel(commandProps()))
|
||||
expect(command.numChildren).to.eq(2)
|
||||
})
|
||||
|
||||
it('with children that are a command group', () => {
|
||||
const command = new CommandModel(commandProps({}))
|
||||
|
||||
command.addChild(new CommandModel(commandProps()))
|
||||
|
||||
const commandGroup = new CommandModel(commandProps())
|
||||
|
||||
commandGroup.addChild(new CommandModel(commandProps()))
|
||||
commandGroup.addChild(new CommandModel(commandProps()))
|
||||
|
||||
command.addChild(commandGroup)
|
||||
expect(command.numChildren).to.eq(4)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('.hasChildren', () => {
|
||||
context('event log', () => {
|
||||
it('with no children', () => {
|
||||
const command = new CommandModel(commandProps({ event: true }))
|
||||
|
||||
expect(command.hasChildren).to.be.false
|
||||
})
|
||||
|
||||
it('with one or more children', () => {
|
||||
const command = new CommandModel(commandProps({ event: true }))
|
||||
|
||||
command.addChild(new CommandModel(commandProps()))
|
||||
|
||||
expect(command.hasChildren).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
context('command log', () => {
|
||||
it('with no children', () => {
|
||||
const command = new CommandModel(commandProps({}))
|
||||
|
||||
expect(command.hasChildren).to.be.false
|
||||
})
|
||||
|
||||
it('with one or more children', () => {
|
||||
const command = new CommandModel(commandProps({}))
|
||||
|
||||
command.addChild(new CommandModel(commandProps()))
|
||||
expect(command.hasChildren).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('.isLongRunning', () => {
|
||||
describe('when model is pending on initialization and LONG_RUNNING_THRESHOLD passes', () => {
|
||||
let command: CommandModel
|
||||
@@ -46,26 +150,26 @@ describe('Command model', () => {
|
||||
expect(command.isLongRunning).to.be.false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when model is not pending on initialization, is updated to pending, and LONG_RUNNING_THRESHOLD passes', () => {
|
||||
let command: CommandModel
|
||||
describe('when model is not pending on initialization, is updated to pending, and LONG_RUNNING_THRESHOLD passes', () => {
|
||||
let command: CommandModel
|
||||
|
||||
beforeEach(() => {
|
||||
command = new CommandModel(commandProps({ state: null }))
|
||||
clock.tick(300)
|
||||
command.update({ state: 'pending' } as CommandProps)
|
||||
})
|
||||
beforeEach(() => {
|
||||
command = new CommandModel(commandProps({ state: null }))
|
||||
clock.tick(300)
|
||||
command.update({ state: 'pending' } as CommandProps)
|
||||
})
|
||||
|
||||
it('sets isLongRunning to true if model is still pending', () => {
|
||||
clock.tick(LONG_RUNNING_THRESHOLD)
|
||||
expect(command.isLongRunning).to.be.true
|
||||
})
|
||||
it('sets isLongRunning to true if model is still pending', () => {
|
||||
clock.tick(LONG_RUNNING_THRESHOLD)
|
||||
expect(command.isLongRunning).to.be.true
|
||||
})
|
||||
|
||||
it('does not set isLongRunning to true if model is no longer pending', () => {
|
||||
command.state = 'passed'
|
||||
clock.tick(LONG_RUNNING_THRESHOLD)
|
||||
expect(command.isLongRunning).to.be.false
|
||||
it('does not set isLongRunning to true if model is no longer pending', () => {
|
||||
command.state = 'passed'
|
||||
clock.tick(LONG_RUNNING_THRESHOLD)
|
||||
expect(command.isLongRunning).to.be.false
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -158,10 +158,16 @@ export const addCommand = (runner: EventEmitter, log: Partial<CommandModel>) =>
|
||||
state: 'passed',
|
||||
testId: 'r3',
|
||||
testCurrentRetry: 0,
|
||||
timeout: 4000,
|
||||
type: 'parent',
|
||||
url: 'http://example.com',
|
||||
hasConsoleProps: true,
|
||||
}
|
||||
|
||||
runner.emit('reporter:log:add', Object.assign(defaultLog, log))
|
||||
const commandLog = Object.assign(defaultLog, log)
|
||||
|
||||
runner.emit('reporter:log:add', commandLog)
|
||||
|
||||
// return command log id to enable adding new command to command group
|
||||
return commandLog.id
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const LONG_RUNNING_THRESHOLD = 1000
|
||||
|
||||
interface RenderProps {
|
||||
message?: string
|
||||
indicator?: string
|
||||
indicator?: 'successful' | 'pending' | 'aborted' | 'bad'
|
||||
interceptions?: Array<{
|
||||
command: 'intercept' | 'route'
|
||||
alias?: string
|
||||
@@ -34,7 +34,6 @@ export interface CommandProps extends InstrumentProps {
|
||||
group?: number
|
||||
hasSnapshot?: boolean
|
||||
hasConsoleProps?: boolean
|
||||
|
||||
}
|
||||
|
||||
export default class Command extends Instrument {
|
||||
@@ -45,10 +44,9 @@ export default class Command extends Instrument {
|
||||
@observable number?: number
|
||||
@observable numElements: number
|
||||
@observable timeout?: number
|
||||
@observable visible?: boolean = true
|
||||
@observable visible?: boolean
|
||||
@observable wallClockStartedAt?: string
|
||||
@observable children: Array<Command> = []
|
||||
@observable isChild = false
|
||||
@observable hookId: string
|
||||
@observable isStudio: boolean
|
||||
@observable showError?: boolean = false
|
||||
@@ -64,9 +62,21 @@ export default class Command extends Instrument {
|
||||
return this.renderProps.message || this.message
|
||||
}
|
||||
|
||||
private countNestedCommands (children) {
|
||||
if (children.length === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return children.length + children.reduce((previousValue, child) => previousValue + this.countNestedCommands(child.children), 0)
|
||||
}
|
||||
|
||||
@computed get numChildren () {
|
||||
// and one to include self so it's the total number of same events
|
||||
return this.children.length + 1
|
||||
if (this.event) {
|
||||
// add one to include self so it's the total number of same events
|
||||
return this.children.length + 1
|
||||
}
|
||||
|
||||
return this.countNestedCommands(this.children)
|
||||
}
|
||||
|
||||
@computed get isOpen () {
|
||||
@@ -75,6 +85,7 @@ export default class Command extends Instrument {
|
||||
return this._isOpen || (this._isOpen === null
|
||||
&& (
|
||||
(this.group && this.type === 'system' && this.hasChildren) ||
|
||||
(this.hasChildren && !this.event && this.type !== 'system') ||
|
||||
_.some(this.children, (v) => v.hasChildren) ||
|
||||
_.last(this.children)?.isOpen ||
|
||||
(_.some(this.children, (v) => v.isLongRunning) && _.last(this.children)?.state === 'pending') ||
|
||||
@@ -88,7 +99,13 @@ export default class Command extends Instrument {
|
||||
}
|
||||
|
||||
@computed get hasChildren () {
|
||||
return this.numChildren > 1
|
||||
if (this.event) {
|
||||
// if the command is an event log, we add one to the number of children count to include
|
||||
// itself in the total number of same events that render when the group is closed
|
||||
return this.numChildren > 1
|
||||
}
|
||||
|
||||
return this.numChildren > 0
|
||||
}
|
||||
|
||||
constructor (props: CommandProps) {
|
||||
@@ -100,15 +117,16 @@ export default class Command extends Instrument {
|
||||
this.numElements = props.numElements
|
||||
this.renderProps = props.renderProps || {}
|
||||
this.timeout = props.timeout
|
||||
this.visible = props.visible
|
||||
// command log that are not associated with elements will not have a visibility
|
||||
// attribute set. i.e. cy.visit(), cy.readFile() or cy.log()
|
||||
this.visible = props.visible === undefined || props.visible
|
||||
this.wallClockStartedAt = props.wallClockStartedAt
|
||||
this.hookId = props.hookId
|
||||
this.isStudio = !!props.isStudio
|
||||
this.showError = props.showError
|
||||
this.showError = !!props.showError
|
||||
this.group = props.group
|
||||
this.hasSnapshot = props.hasSnapshot
|
||||
this.hasConsoleProps = props.hasConsoleProps
|
||||
|
||||
this.hasSnapshot = !!props.hasSnapshot
|
||||
this.hasConsoleProps = !!props.hasConsoleProps
|
||||
this._checkLongRunning()
|
||||
}
|
||||
|
||||
@@ -119,7 +137,9 @@ export default class Command extends Instrument {
|
||||
this.event = props.event
|
||||
this.numElements = props.numElements
|
||||
this.renderProps = props.renderProps || {}
|
||||
this.visible = props.visible
|
||||
// command log that are not associated with elements will not have a visibility
|
||||
// attribute set. i.e. cy.visit(), cy.readFile() or cy.log()
|
||||
this.visible = props.visible === undefined || props.visible
|
||||
this.timeout = props.timeout
|
||||
this.hasSnapshot = props.hasSnapshot
|
||||
this.hasConsoleProps = props.hasConsoleProps
|
||||
@@ -142,7 +162,6 @@ export default class Command extends Instrument {
|
||||
}
|
||||
|
||||
addChild (command: Command) {
|
||||
command.isChild = true
|
||||
command.setGroup(this.id)
|
||||
this.children.push(command)
|
||||
}
|
||||
|
||||
@@ -22,13 +22,22 @@ const md = new Markdown()
|
||||
const displayName = (model: CommandModel) => model.displayName || model.name
|
||||
const nameClassName = (name: string) => name.replace(/(\s+)/g, '-')
|
||||
const formattedMessage = (message: string) => message ? md.renderInline(message) : ''
|
||||
const visibleMessage = (model: CommandModel) => {
|
||||
if (model.visible) return ''
|
||||
const invisibleMessage = (model: CommandModel) => {
|
||||
if (model.visible) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return model.numElements > 1 ?
|
||||
'One or more matched elements are not visible.' :
|
||||
'This element is not visible.'
|
||||
}
|
||||
const numberOfChildrenMessage = (numChildren, event?: boolean) => {
|
||||
if (event) {
|
||||
return `This event occurred ${numChildren} times`
|
||||
}
|
||||
|
||||
return `${numChildren} ${numChildren > 1 ? 'logs' : 'log'} currently hidden`
|
||||
}
|
||||
|
||||
const shouldShowCount = (aliasesWithDuplicates: Array<Alias> | null, aliasName: Alias, model: CommandModel) => {
|
||||
if (model.aliasType !== 'route') {
|
||||
@@ -216,17 +225,22 @@ class Command extends Component<Props> {
|
||||
return <TestError model={model} onPrintToConsole={this._onClick}/>
|
||||
}
|
||||
|
||||
const commandName = model.name ? nameClassName(model.name) : ''
|
||||
const isSystemEvent = model.type === 'system' && model.event
|
||||
const isSessionCommand = commandName === 'session'
|
||||
const displayNumOfChildren = !isSystemEvent && !isSessionCommand && model.hasChildren && !model.isOpen
|
||||
|
||||
return (
|
||||
<li
|
||||
className={cs(
|
||||
'command',
|
||||
`command-name-${model.name ? nameClassName(model.name) : ''}`,
|
||||
`command-name-${commandName}`,
|
||||
`command-state-${model.state}`,
|
||||
`command-type-${model.type}`,
|
||||
{
|
||||
'command-is-studio': model.isStudio,
|
||||
'command-is-event': !!model.event,
|
||||
'command-is-invisible': model.visible != null && !model.visible,
|
||||
'command-is-invisible': !model.visible,
|
||||
'command-has-num-elements': model.state !== 'pending' && model.numElements != null,
|
||||
'command-is-pinned': this._isPinned(),
|
||||
'command-with-indicator': !!model.renderProps.indicator,
|
||||
@@ -236,7 +250,6 @@ class Command extends Component<Props> {
|
||||
'command-has-console-props': model.hasConsoleProps,
|
||||
'multiple-elements': model.numElements > 1,
|
||||
'command-has-children': model.hasChildren,
|
||||
'command-is-child': model.isChild,
|
||||
'command-is-open': this._isOpen(),
|
||||
},
|
||||
)}
|
||||
@@ -258,9 +271,11 @@ class Command extends Component<Props> {
|
||||
<i className='fas fa-spinner fa-spin' />
|
||||
<span>{model.number || ''}</span>
|
||||
</span>
|
||||
<span className='command-pin'>
|
||||
<i className='fas fa-thumbtack' />
|
||||
</span>
|
||||
{!model.hasChildren && (
|
||||
<span className='command-pin'>
|
||||
<i className='fas fa-thumbtack' />
|
||||
</span>
|
||||
)}
|
||||
<span className='command-method'>
|
||||
<span>{model.event && model.type !== 'system' ? `(${displayName(model)})` : displayName(model)}</span>
|
||||
</span>
|
||||
@@ -269,7 +284,7 @@ class Command extends Component<Props> {
|
||||
</span>
|
||||
<span className='command-controls'>
|
||||
<i className='far fa-times-circle studio-command-remove' onClick={this._removeStudioCommand} />
|
||||
<Tooltip placement='top' title={visibleMessage(model)} className='cy-tooltip'>
|
||||
<Tooltip placement='top' title={invisibleMessage(model)} className='cy-tooltip'>
|
||||
<i className='command-invisible far fa-eye-slash' />
|
||||
</Tooltip>
|
||||
<Tooltip placement='top' title={`${model.numElements} matched elements`} className='cy-tooltip'>
|
||||
@@ -278,9 +293,11 @@ class Command extends Component<Props> {
|
||||
<span className='alias-container'>
|
||||
<Interceptions model={model} />
|
||||
<Aliases model={model} aliasesWithDuplicates={aliasesWithDuplicates} isOpen={this._isOpen()} />
|
||||
<Tooltip placement='top' title={`This event occurred ${model.numChildren} times`} className='cy-tooltip'>
|
||||
<span className={cs('num-children', { 'has-alias': model.alias, 'has-children': model.numChildren > 1 })}>{model.numChildren}</span>
|
||||
</Tooltip>
|
||||
{displayNumOfChildren && (
|
||||
<Tooltip placement='top' title={numberOfChildrenMessage(model.numChildren, model.event)} className='cy-tooltip'>
|
||||
<span className={cs('num-children', { 'has-alias': model.alias, 'has-children': model.numChildren > 1 })}>{model.numChildren}</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</span>
|
||||
|
||||
</span>
|
||||
@@ -376,6 +393,13 @@ class Command extends Component<Props> {
|
||||
_snapshot (show: boolean) {
|
||||
const { model, runnablesStore } = this.props
|
||||
|
||||
// do not trigger the show:snapshot event for commands groups
|
||||
// TODO: remove this behavior in 10.0+ when a group
|
||||
// can both be expanded and collapsed and pinned
|
||||
if (model.hasChildren) {
|
||||
return
|
||||
}
|
||||
|
||||
if (show) {
|
||||
runnablesStore.attemptingShowSnapshot = true
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
}
|
||||
|
||||
.command {
|
||||
background-color: #eef1f4;
|
||||
cursor: default;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -82,23 +83,12 @@
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.command-type-child {
|
||||
.command-method {
|
||||
&:before {
|
||||
float: left;
|
||||
content: "-";
|
||||
margin-right: 2px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.command-type-system.command-is-event,
|
||||
.command-name-session {
|
||||
> span > .command-wrapper {
|
||||
.num-children {
|
||||
display: none;
|
||||
}
|
||||
.command-type-child > span > div > div > .command-method {
|
||||
&:before {
|
||||
float: left;
|
||||
content: "-";
|
||||
margin-right: 2px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,18 +517,12 @@
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.command-has-num-elements .num-elements,
|
||||
.num-children,
|
||||
.command-has-children.command-is-open
|
||||
> span
|
||||
> .command-wrapper
|
||||
.num-children.has-children {
|
||||
.command-has-num-elements .num-elements {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.command-has-num-elements.no-elements .num-elements,
|
||||
.command-has-num-elements.multiple-elements .num-elements,
|
||||
.command-has-children .num-children.has-children {
|
||||
.command-has-num-elements.multiple-elements .num-elements {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
.num-children {
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
display: none;
|
||||
font-size: 85%;
|
||||
line-height: 1;
|
||||
margin-left: 5px;
|
||||
@@ -18,6 +17,7 @@
|
||||
|
||||
.num-elements {
|
||||
background-color: #ababab;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.num-children {
|
||||
|
||||
Reference in New Issue
Block a user