Skip to content

Commit

Permalink
chore: add internal log group API and update log group styles (#20857)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Henkes <mjhenkes@gmail.com>
  • Loading branch information
emilyrohrbough and mjhenkes committed Apr 5, 2022
1 parent ee7495f commit d0fc93b
Show file tree
Hide file tree
Showing 22 changed files with 701 additions and 125 deletions.
3 changes: 2 additions & 1 deletion circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ macWorkflowFilters: &mac-workflow-filters
when:
or:
- equal: [ develop, << 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 >>
Expand Down
1 change: 1 addition & 0 deletions cli/types/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5712,6 +5712,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
Expand Down
2 changes: 1 addition & 1 deletion packages/driver/src/cy/commands/xhr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
42 changes: 42 additions & 0 deletions packages/driver/src/cy/logGroup.ts
Original file line number Diff line number Diff line change
@@ -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)
}
4 changes: 2 additions & 2 deletions packages/driver/src/cy/net-stubbing/route-matcher-log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 3 additions & 1 deletion packages/driver/src/cypress/error_messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
27 changes: 19 additions & 8 deletions packages/driver/src/cypress/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,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
Expand All @@ -127,7 +127,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'
Expand Down Expand Up @@ -202,26 +202,26 @@ 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
}

class Log {
cy: any
state: any
state: Cypress.State
config: any
fireChangeEvent: ((log) => (void | undefined))
obj: any
Expand Down Expand Up @@ -373,6 +373,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,
Expand Down Expand Up @@ -401,6 +408,10 @@ class Log {
return this
}

endGroup () {
this.state('logGroupIds', _.slice(this.state('logGroupIds'), 0, -1))
}

getError (err) {
return err.stack || err.message
}
Expand Down
6 changes: 3 additions & 3 deletions packages/driver/src/cypress/proxy-logging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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)

Expand Down
7 changes: 3 additions & 4 deletions packages/driver/src/cypress/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1773,10 +1773,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)
Expand Down
27 changes: 27 additions & 0 deletions packages/driver/types/cy/logGroup.d.ts
Original file line number Diff line number Diff line change
@@ -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']
}
}
}
62 changes: 62 additions & 0 deletions packages/driver/types/cypress/log.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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
}

interface InternalLogConfig {
// defaults to command
instrument?: 'agent' | 'command' | 'route'
// name of the log
name?: string
// the name override for display purposes only
displayName?: string
// 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[]
// whether or not the xhr route had a corresponding response stubbed out
isStubbed?: boolean
alias?: string
aliasType?: 'agent' | 'route' | 'primitive' | 'dom' | undefined
commandName?: string
// the JQuery element for the command. This will highlight the command
// in the main window when debugging
$el?: JQuery
// 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
// whether or not to start a new log group
groupStart?: boolean
// 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')
// whether or not the generated log was an event or command
event?: boolean
method?: string
url?: string
status?: number
// the number of xhr responses that occurred. This is only applicable to
// logs defined with instrument=route
numResponses?: number
response?: string | object
// provide the content to display in the dev tool's console when a log is
// clicked from the Reporter's Command Log
consoleProps?: () => ObjectLike
renderProps?: () => {
indicator?: 'aborted' | 'pending' | 'successful' | 'bad'
message?: string
}
browserPreRequest?: any
// timeout of the group command - defaults to defaultCommandTimeout
timeout?: number
}
}
29 changes: 4 additions & 25 deletions packages/driver/types/internal-types.d.ts
Original file line number Diff line number Diff line change
@@ -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" />

declare namespace Cypress {
interface Actions {
Expand Down Expand Up @@ -36,7 +38,6 @@ declare namespace Cypress {
sinon: sinon.SinonApi
utils: CypressUtils
state: State

originalConfig: Record<string, any>
}

Expand All @@ -46,40 +47,18 @@ declare namespace Cypress {
warning: (message: string) => void
}

type Log = ReturnType<Cypress.log>

interface LogConfig {
message: any[]
instrument?: 'route'
isStubbed?: boolean
alias?: string
aliasType?: 'route'
commandName?: string
type?: 'parent'
event?: boolean
method?: string
url?: string
status?: number
numResponses?: number
response?: string | object
renderProps?: () => {
indicator?: 'aborted' | 'pending' | 'successful' | 'bad'
message?: string
}
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
}

// Extend Cypress.state properties here
interface ResolvedConfigOptions {
$autIframe: JQuery<HTMLIFrameElement>
document: Document
Expand Down
2 changes: 1 addition & 1 deletion packages/reporter/cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
12 changes: 12 additions & 0 deletions packages/reporter/cypress/fixtures/runnables_commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down

3 comments on commit d0fc93b

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on d0fc93b Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/9.5.4/linux-x64/develop-d0fc93b0dc5c59e85a49d3e097d30980077294f2/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on d0fc93b Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/9.5.4/darwin-x64/develop-d0fc93b0dc5c59e85a49d3e097d30980077294f2/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on d0fc93b Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/9.5.4/win32-x64/develop-d0fc93b0dc5c59e85a49d3e097d30980077294f2/cypress.tgz

Please sign in to comment.