Skip to content

Commit 0532f92

Browse files
authored
chore: Open in IDE button for block and hook definitions in Runner UI (#30859)
* init vitest unit test harness for @packages/driver * unit tests with stack examples * drop lines until __cypress/tests rather than just __cypress * changelog * changelog * remove vitest globals (unnecessary) from driver tsconfig * bump the number of junit reports expected from the unit tests job * fix ts-check error in scroll.ts * fix type definition for getInvocationDetails * rm packageManager key on package.json * remove junit reporter, as script that verifies result does not recognize it as valid * change @ts-expect-error to @ts-ignore for .scroll, as this ts check is apparently flaky * set expected mocha result back to 19 * add ct style stacks for cy in cy ct tests * re-enable junit reporter, update mocha result verification to be more lenient about the order of xml fields * persist binaries for this branch * expect 20 junit reports again * fix mocha v vitest verification? * add binary system test to verify correct file paths for codepoints in protocol events * fix invocation details system test filename * add required config for binary tests * build on darwin to fix binary system tests * build linux-arm64 for this branch * simplify binary test * build windows binary * rm binary system test * Update cli/CHANGELOG.md
1 parent 249cfde commit 0532f92

File tree

12 files changed

+541
-44
lines changed

12 files changed

+541
-44
lines changed

.circleci/workflows.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ macWorkflowFilters: &darwin-workflow-filters
4444
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
4545
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
4646
- equal: [ 'chore/update_vue_test_utils', << pipeline.git.branch >> ]
47+
- equal: [ 'cacie/fix-hook-test-stack-analysis', << pipeline.git.branch >> ]
4748
- matches:
4849
pattern: /^release\/\d+\.\d+\.\d+$/
4950
value: << pipeline.git.branch >>
@@ -55,6 +56,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
5556
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
5657
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
5758
- equal: [ 'chore/update_binary_branch', << pipeline.git.branch >> ]
59+
- equal: [ 'cacie/fix-hook-test-stack-analysis', << pipeline.git.branch >> ]
5860
- matches:
5961
pattern: /^release\/\d+\.\d+\.\d+$/
6062
value: << pipeline.git.branch >>
@@ -78,6 +80,7 @@ windowsWorkflowFilters: &windows-workflow-filters
7880
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
7981
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
8082
- equal: [ 'ryanm/chore/electron-33-upgrade', << pipeline.git.branch >> ]
83+
- equal: [ 'cacie/fix-hook-test-stack-analysis', << pipeline.git.branch >> ]
8184
- matches:
8285
pattern: /^release\/\d+\.\d+\.\d+$/
8386
value: << pipeline.git.branch >>
@@ -153,7 +156,7 @@ commands:
153156
name: Set environment variable to determine whether or not to persist artifacts
154157
command: |
155158
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
156-
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/update_vue_test_utils" ]]; then
159+
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/update_vue_test_utils" && "$CIRCLE_BRANCH" != cacie/fix-hook-test-stack-analysis ]]; then
157160
export SHOULD_PERSIST_ARTIFACTS=true
158161
fi' >> "$BASH_ENV"
159162
# You must run `setup_should_persist_artifacts` command and be using bash before running this command
@@ -1678,7 +1681,7 @@ jobs:
16781681
# run type checking for each individual package
16791682
- run: yarn lerna run types
16801683
- verify-mocha-results:
1681-
expectedResultCount: 19
1684+
expectedResultCount: 20
16821685
- store_test_results:
16831686
path: /tmp/cypress
16841687
# CLI tests generate HTML files with sample CLI command output

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"stop-only": "npx stop-only --skip .cy,.publish,.projects,node_modules,dist,dist-test,fixtures,lib,bower_components,src,__snapshots__ --exclude cypress-tests.ts,*only.cy.js",
5555
"stop-only-all": "yarn stop-only --folder packages",
5656
"pretest": "yarn ensure-deps",
57-
"test": "yarn lerna exec yarn test --scope=cypress --scope=@packages/{config,data-context,electron,errors,extension,https-proxy,launcher,net-stubbing,network,packherd-require,proxy,rewriter,scaffold-config,socket,v8-snapshot-require,telemetry} --scope=@tooling/{electron-mksnapshot,v8-snapshot}",
57+
"test": "yarn lerna exec yarn test --scope=cypress --scope=@packages/{config,data-context,driver,electron,errors,extension,https-proxy,launcher,net-stubbing,network,packherd-require,proxy,rewriter,scaffold-config,socket,v8-snapshot-require,telemetry} --scope=@tooling/{electron-mksnapshot,v8-snapshot}",
5858
"test-debug": "lerna exec yarn test-debug --ignore=@packages/{driver,root,static,web-config}",
5959
"test-integration": "lerna exec yarn test-integration --ignore=@packages/{driver,root,static,web-config}",
6060
"test-mocha": "mocha --reporter spec scripts/spec.js",

packages/driver/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
"cypress:run:inject-document-domain": "node ../../scripts/cypress run --config-file ./cypress.config-injectDocumentDomain.ts",
1111
"postinstall": "patch-package",
1212
"lint": "eslint --ext .js,.jsx,.ts,.tsx,.json, .",
13-
"start": "node -e 'console.log(require(`chalk`).red(`\nError:\n\tRunning \\`yarn start\\` is no longer needed for driver/cypress tests.\n\tWe now automatically spawn the server in e2e.setupNodeEvents config.\n\tChanges to the server will be watched and reloaded automatically.`))'"
13+
"start": "node -e 'console.log(require(`chalk`).red(`\nError:\n\tRunning \\`yarn start\\` is no longer needed for driver/cypress tests.\n\tWe now automatically spawn the server in e2e.setupNodeEvents config.\n\tChanges to the server will be watched and reloaded automatically.`))'",
14+
"test": "vitest run",
15+
"test:watch": "vitest watch"
1416
},
1517
"dependencies": {},
1618
"devDependencies": {
@@ -58,6 +60,7 @@
5860
"jimp": "0.22.12",
5961
"jquery": "3.7.1",
6062
"js-cookie": "3.0.5",
63+
"jsdom": "^26.0.0",
6164
"json-stable-stringify": "1.0.1",
6265
"lodash": "^4.17.21",
6366
"md5": "2.3.0",
@@ -81,6 +84,7 @@
8184
"url-parse": "1.5.10",
8285
"vanilla-text-mask": "5.1.1",
8386
"vite": "5.2.11",
87+
"vitest": "^2.1.8",
8488
"webpack": "^5.88.2",
8589
"zone.js": "0.9.0"
8690
},

packages/driver/src/cy/commands/actions/scroll.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ export default (Commands, Cypress, cy, state) => {
128128
const scrollIntoView = () => {
129129
return new Promise((resolve, reject) => {
130130
// scroll our axes
131+
// @ts-ignore - scrollTo does not define a 'done()' key on its config object.
131132
return $(options.$parent).scrollTo(options.$el, {
132133
axis: options.axis,
133134
easing: options.easing,

packages/driver/src/cypress/stack_utils.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,17 @@ const stackWithUserInvocationStackSpliced = (err, userInvocationStack): StackAnd
108108
}
109109
}
110110

111-
type InvocationDetails = MessageLineDetail | {}
111+
type InvocationDetails = {
112+
absoluteFile?: string
113+
column?: number
114+
line?: number
115+
originalFile?: string
116+
relativeFile?: string
117+
stack: string
118+
}
112119

113-
const getInvocationDetails = (specWindow, config) => {
120+
// used to determine codeframes for hook/test/etc definitions rather than command invocations
121+
const getInvocationDetails = (specWindow, config): InvocationDetails | undefined => {
114122
if (specWindow.Error) {
115123
let stack = (new specWindow.Error()).stack
116124

@@ -120,10 +128,19 @@ const getInvocationDetails = (specWindow, config) => {
120128
// firefox and chrome throw stacks that include lines from cypress
121129
// So we drop the lines until we get to the spec stackframe (includes __cypress)
122130
if (specWindow.Cypress) {
123-
stack = stackWithLinesDroppedFromMarker(stack, '__cypress', true)
131+
// The stack includes frames internal to cypress, after the spec stackframe. In order
132+
// to determine the invocation details, the stack needs to be parsed and trimmed.
133+
134+
// in Chrome and Firefox in E2E contexts, the spec stackframe includes the pattern, '__cypress/tests'.
135+
if (stack.includes('__cypress/tests')) {
136+
stack = stackWithLinesDroppedFromMarker(stack, '__cypress/tests', true)
137+
} else {
138+
// CT error contexts include the `__cypress` marker but not the `/tests` portion
139+
stack = stackWithLinesDroppedFromMarker(stack, '__cypress', true)
140+
}
124141
}
125142

126-
const details: InvocationDetails = getSourceDetailsForFirstLine(stack, config('projectRoot')) || {};
143+
const details: Omit<InvocationDetails, 'stack'> = getSourceDetailsForFirstLine(stack, config('projectRoot')) || {};
127144

128145
(details as any).stack = stack
129146

packages/driver/test/unit/cypress/__fixtures__/spec_stackframes.json

Lines changed: 43 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @vitest-environment jsdom
3+
*/
4+
import { vi, describe, it, expect, beforeEach } from 'vitest'
5+
6+
import source_map_utils from '../../../src/cypress/source_map_utils'
7+
import stack_utils from '../../../src/cypress/stack_utils'
8+
import stackFrameFixture from './__fixtures__/spec_stackframes.json'
9+
10+
vi.mock('../../../src/cypress/source_map_utils', () => {
11+
return {
12+
default: {
13+
getSourcePosition: vi.fn(),
14+
},
15+
}
16+
})
17+
18+
describe('stack_utils', () => {
19+
beforeEach(() => {
20+
// @ts-expect-error
21+
global.Cypress = {
22+
config: vi.fn(),
23+
}
24+
25+
vi.resetAllMocks()
26+
})
27+
28+
describe('getInvocationDetails', () => {
29+
const { line, column, scenarios } = stackFrameFixture
30+
31+
const projectRoot = '/foo/bar'
32+
33+
let stack: string
34+
35+
class MockError {
36+
get stack () {
37+
return stack
38+
}
39+
}
40+
const config = () => projectRoot
41+
42+
for (const scenario of scenarios) {
43+
const { browser, build, specFrame, stack: scenarioStack } = scenario
44+
45+
describe(`${browser}:${build}`, () => {
46+
beforeEach(() => {
47+
stack = scenarioStack
48+
})
49+
50+
it('calls getSourcePosition with the correct file, line, and column', () => {
51+
stack_utils.getInvocationDetails(
52+
{ Error: MockError, Cypress: {} },
53+
config,
54+
)
55+
56+
// getSourcePosition is not called directly from getInvocationDetails, but via:
57+
// - getSourceDetailsForFirstLine
58+
// - getSourceDetailsForLine
59+
expect(source_map_utils.getSourcePosition).toHaveBeenCalledWith(specFrame, expect.objectContaining({
60+
column,
61+
line,
62+
file: specFrame,
63+
}))
64+
})
65+
})
66+
}
67+
})
68+
})

packages/driver/tsconfig.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
"noEmit": true,
1313
"outDir": "dist",
1414
"noErrorTruncation": true,
15-
"types": []
15+
"types": [],
16+
"resolveJsonModule": true
1617
},
1718
"exclude": [
18-
"dist"
19+
"dist",
20+
"test"
1921
]
2022
}

packages/driver/vitest.config.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineConfig } from 'vitest/config'
2+
3+
export default defineConfig({
4+
test: {
5+
include: ['test/unit/**/*.spec.ts'],
6+
environment: 'jsdom',
7+
exclude: ['**/__fixtures__/**/*'],
8+
reporters: [
9+
'default',
10+
['junit', { suiteName: 'Driver Unit Tests', outputFile: '/tmp/cypress/junit/driver-test-results.xml' }],
11+
],
12+
},
13+
})

scripts/verify-mocha-results.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,34 @@ const la = require('lazy-ass')
1111
const path = require('path')
1212
const { readCircleEnv } = require('./circle-env')
1313

14-
const RESULT_REGEX = /<testsuites name="([^"]+)" time="([^"]+)" tests="([^"]+)" failures="([^"]+)"(?: skipped="([^"]+)"|)>/
14+
// mocha regex
15+
const MOCHA_REGEX = /<testsuites name="([^"]+)" time="([^"]+)" tests="([^"]+)" failures="([^"]+)"(?: skipped="([^"]+)"|)>/
16+
// vitest regex
17+
const VITEST_REGEX = /<testsuites name="([^"]+)" tests="([^"]+)" failures="([^"]+)" errors="([^"]+)" time="([^"]+)"(?: skipped="([^"]+)"|)>/
18+
1519
const REPORTS_PATH = '/tmp/cypress/junit'
1620

1721
const expectedResultCount = Number(process.argv[process.argv.length - 1])
1822

19-
const parseResult = (xml) => {
20-
const [name, time, tests, failures, skipped] = RESULT_REGEX.exec(xml).slice(1)
23+
const parseMochaResult = (xml) => {
24+
const [name, time, tests, failures, skipped] = MOCHA_REGEX.exec(xml).slice(1)
25+
26+
return {
27+
name, time, tests: Number(tests), failures: Number(failures), skipped: Number(skipped || 0),
28+
}
29+
}
30+
const parseVitestResult = (xml) => {
31+
const [name, tests, failures, , time, skipped] = VITEST_REGEX.exec(xml).slice(1)
2132

2233
return {
2334
name, time, tests: Number(tests), failures: Number(failures), skipped: Number(skipped || 0),
2435
}
2536
}
2637

38+
const parseResult = (xml) => {
39+
return MOCHA_REGEX.test(xml) ? parseMochaResult(xml) : parseVitestResult(xml)
40+
}
41+
2742
const total = { tests: 0, failures: 0, skipped: 0 }
2843

2944
console.log(`Looking for reports in ${REPORTS_PATH}`)

system-tests/lib/serverStub.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export const routeHandlers: Record<string, RouteHandler> = {
269269
},
270270
}
271271

272-
export const createRoutes = (props: DeepPartial<typeof routeHandlers>) => {
272+
export const createRoutes = (props: DeepPartial<typeof routeHandlers> = {}) => {
273273
return _.values(_.merge(_.cloneDeep(routeHandlers), props))
274274
}
275275

0 commit comments

Comments
 (0)