mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-25 18:41:08 -06:00
fix: relativeFile is now relative to repo root (#23833)
Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com> Co-authored-by: Tim Griesser <tgriesser10@gmail.com>
This commit is contained in:
1
cli/types/cypress.d.ts
vendored
1
cli/types/cypress.d.ts
vendored
@@ -2999,6 +2999,7 @@ declare namespace Cypress {
|
||||
// Internal or Unlisted at server/lib/config_options
|
||||
namespace: string
|
||||
projectRoot: string
|
||||
repoRoot: string | null
|
||||
devServerPublicPathRoute: string
|
||||
cypressBinaryRoot: string
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1
|
||||
"port": null,
|
||||
"projectId": null,
|
||||
"redirectionLimit": 20,
|
||||
"repoRoot": null,
|
||||
"reporter": "spec",
|
||||
"reporterOptions": null,
|
||||
"requestTimeout": 5000,
|
||||
@@ -136,6 +137,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys f
|
||||
"port": null,
|
||||
"projectId": null,
|
||||
"redirectionLimit": 20,
|
||||
"repoRoot": null,
|
||||
"reporter": "spec",
|
||||
"reporterOptions": null,
|
||||
"requestTimeout": 5000,
|
||||
|
||||
@@ -501,6 +501,11 @@ const runtimeOptions: Array<RuntimeConfigOption> = [
|
||||
defaultValue: '__cypress',
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
}, {
|
||||
name: 'repoRoot',
|
||||
defaultValue: null,
|
||||
validation: validate.isString,
|
||||
isInternal: true,
|
||||
}, {
|
||||
name: 'reporterRoute',
|
||||
defaultValue: '/__cypress/reporter',
|
||||
|
||||
@@ -21,7 +21,7 @@ const debug = Debug('cypress:config:project')
|
||||
// TODO: any -> SetupFullConfigOptions in data-context/src/data/ProjectConfigManager.ts
|
||||
export function setupFullConfigWithDefaults (obj: any = {}, getFilesByGlob: any): Promise<FullConfig> {
|
||||
debug('setting config object %o', obj)
|
||||
let { projectRoot, projectName, config, envFile, options, cliConfig } = obj
|
||||
let { projectRoot, projectName, config, envFile, options, cliConfig, repoRoot } = obj
|
||||
|
||||
// just force config to be an object so we dont have to do as much
|
||||
// work in our tests
|
||||
@@ -35,6 +35,7 @@ export function setupFullConfigWithDefaults (obj: any = {}, getFilesByGlob: any)
|
||||
config.envFile = envFile
|
||||
config.projectRoot = projectRoot
|
||||
config.projectName = projectName
|
||||
config.repoRoot = repoRoot
|
||||
|
||||
// @ts-ignore
|
||||
return mergeDefaults(config, options, cliConfig, getFilesByGlob)
|
||||
|
||||
@@ -98,6 +98,10 @@ export class DataContext {
|
||||
this.lifecycleManager = new ProjectLifecycleManager(this)
|
||||
}
|
||||
|
||||
get git () {
|
||||
return this.coreData.currentProjectGitInfo
|
||||
}
|
||||
|
||||
get schema () {
|
||||
return this._config.schema
|
||||
}
|
||||
|
||||
@@ -461,6 +461,16 @@ export class ProjectConfigManager {
|
||||
)
|
||||
}
|
||||
|
||||
get repoRoot () {
|
||||
/*
|
||||
Used to detect the correct file path when a test fails.
|
||||
It is derived and assigned in the packages/driver in stack_utils.
|
||||
It's needed to show the correct link to files in repo mgmt tools like GitHub in the dashboard.
|
||||
Right now we assume the repoRoot is where the `.git` dir is located.
|
||||
*/
|
||||
return this.options.ctx.git?.gitBaseDir
|
||||
}
|
||||
|
||||
private async buildBaseFullConfig (configFileContents: Cypress.ConfigOptions, envFile: Cypress.ConfigOptions, options: Partial<AllModeOptions>, withBrowsers = true) {
|
||||
assert(this._testingType, 'Cannot build base full config without a testing type')
|
||||
this.validateConfigRoot(configFileContents, this._testingType)
|
||||
@@ -479,6 +489,7 @@ export class ProjectConfigManager {
|
||||
cliConfig: options.config ?? {},
|
||||
projectName: path.basename(this.options.projectRoot),
|
||||
projectRoot: this.options.projectRoot,
|
||||
repoRoot: this.repoRoot,
|
||||
config: _.cloneDeep(configFileContents),
|
||||
envFile: _.cloneDeep(envFile),
|
||||
options: {
|
||||
|
||||
@@ -395,19 +395,17 @@ export class ProjectLifecycleManager {
|
||||
this.ctx.update((s) => {
|
||||
s.currentProject = projectRoot
|
||||
s.currentProjectGitInfo?.destroy()
|
||||
if (!this.ctx.isRunMode) {
|
||||
s.currentProjectGitInfo = new GitDataSource({
|
||||
isRunMode: this.ctx.isRunMode,
|
||||
projectRoot,
|
||||
onError: this.ctx.onError,
|
||||
onBranchChange: () => {
|
||||
this.ctx.emitter.branchChange()
|
||||
},
|
||||
onGitInfoChange: (specPaths) => {
|
||||
this.ctx.emitter.gitInfoChange(specPaths)
|
||||
},
|
||||
})
|
||||
}
|
||||
s.currentProjectGitInfo = new GitDataSource({
|
||||
isRunMode: this.ctx.isRunMode,
|
||||
projectRoot,
|
||||
onError: this.ctx.onError,
|
||||
onBranchChange: () => {
|
||||
this.ctx.emitter.branchChange()
|
||||
},
|
||||
onGitInfoChange: (specPaths) => {
|
||||
this.ctx.emitter.gitInfoChange(specPaths)
|
||||
},
|
||||
})
|
||||
|
||||
s.diagnostics = { error: null, warnings: [] }
|
||||
s.packageManager = packageManagerUsed
|
||||
|
||||
@@ -93,7 +93,13 @@ export class GitDataSource {
|
||||
debug('exception caught when loading git client')
|
||||
}
|
||||
|
||||
if (!config.isRunMode) {
|
||||
// don't watch/refresh git data in run mode since we only
|
||||
// need it to detect the .git directory to set `repoRoot`
|
||||
if (config.isRunMode) {
|
||||
this.#verifyGitRepo().catch(() => {
|
||||
// Empty catch for no-floating-promises rule
|
||||
})
|
||||
} else {
|
||||
this.#refreshAllGitData()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,58 @@ describe('driver/src/cypress/stack_utils', () => {
|
||||
})
|
||||
})
|
||||
|
||||
context('getRelativePathFromRoot', () => {
|
||||
const relativeFile = 'relative/path/to/file.js'
|
||||
const absoluteFile = 'User/ruby/cypress/packages/driver/relative/path/to/file.js'
|
||||
const repoRoot = 'User/ruby/cypress'
|
||||
const relativePathFromRoot = 'packages/driver/relative/path/to/file.js'
|
||||
|
||||
const actualPlatform = Cypress.config('platform')
|
||||
const actualRepoRoot = Cypress.config('repoRoot')
|
||||
|
||||
after(() => {
|
||||
// restore config values to prevent bleeding into subsequent tests
|
||||
Cypress.config('platform', actualPlatform)
|
||||
Cypress.config('repoRoot', actualRepoRoot)
|
||||
})
|
||||
|
||||
it('returns relativeFile if absoluteFile is empty', () => {
|
||||
const result = $stackUtils.getRelativePathFromRoot(relativeFile, undefined)
|
||||
|
||||
expect(result).to.equal(relativeFile)
|
||||
})
|
||||
|
||||
it('returns relativeFile if `repoRoot` is not set in the config', () => {
|
||||
const result = $stackUtils.getRelativePathFromRoot(relativeFile, absoluteFile)
|
||||
|
||||
expect(result).to.equal(relativeFile)
|
||||
})
|
||||
|
||||
it('returns relativeFile if absoluteFile does not start with `repoRoot`', () => {
|
||||
Cypress.config('repoRoot', 'User/ruby/test-repo')
|
||||
const result = $stackUtils.getRelativePathFromRoot(relativeFile, absoluteFile)
|
||||
|
||||
expect(result).to.equal(relativeFile)
|
||||
})
|
||||
|
||||
it('returns the relative path from root if the absoluteFile starts with `repoRoot`', () => {
|
||||
Cypress.config('repoRoot', repoRoot)
|
||||
const result = $stackUtils.getRelativePathFromRoot(relativeFile, absoluteFile)
|
||||
|
||||
expect(result).to.equal(relativePathFromRoot)
|
||||
})
|
||||
|
||||
it('uses posix on windows', () => {
|
||||
Cypress.config('repoRoot', 'C:/Users/Administrator/Documents/GitHub/cypress')
|
||||
Cypress.config('platform', 'win32')
|
||||
const absoluteFile = 'C:\\Users\\Administrator\\Documents\\GitHub\\cypress\\packages\\app/cypress/e2e/reporter_header.cy.ts'
|
||||
const relativeFile = 'cypress/e2e/reporter_header.cy.ts'
|
||||
const result = $stackUtils.getRelativePathFromRoot(relativeFile, absoluteFile)
|
||||
|
||||
expect(result).to.equal('packages/app/cypress/e2e/reporter_header.cy.ts')
|
||||
})
|
||||
})
|
||||
|
||||
context('.getCodeFrame', () => {
|
||||
let originalErr
|
||||
const sourceCode = `it('is a failing test', () => {
|
||||
@@ -93,6 +145,14 @@ describe('driver/src/cypress/stack_utils', () => {
|
||||
|
||||
expect($stackUtils.getCodeFrame(originalErr)).to.be.undefined
|
||||
})
|
||||
|
||||
it('relativeFile is relative to the repo root when `absoluteFile` starts with `repoRoot`', () => {
|
||||
Cypress.config('repoRoot', '/dev')
|
||||
cy.stub($sourceMapUtils, 'getSourceContents').returns(sourceCode)
|
||||
const codeFrame = $stackUtils.getCodeFrame(originalErr)
|
||||
|
||||
expect(codeFrame.relativeFile).to.equal('app/cypress/integration/features/source_map_spec.js')
|
||||
})
|
||||
})
|
||||
|
||||
context('.getSourceStack when http links', () => {
|
||||
|
||||
@@ -142,13 +142,28 @@ const getCodeFrameFromSource = (sourceCode, { line, column: originalColumn, rela
|
||||
line,
|
||||
column,
|
||||
originalFile: relativeFile,
|
||||
relativeFile,
|
||||
relativeFile: getRelativePathFromRoot(relativeFile, absoluteFile),
|
||||
absoluteFile,
|
||||
frame,
|
||||
language: getLanguageFromExtension(relativeFile),
|
||||
}
|
||||
}
|
||||
|
||||
const getRelativePathFromRoot = (relativeFile, absoluteFile) => {
|
||||
// at this point relativeFile is relative to the cypress config
|
||||
// we need it to be relative to the repo root, which is different for monorepos
|
||||
const repoRoot = Cypress.config('repoRoot')
|
||||
const posixAbsoluteFile = (Cypress.config('platform') === 'win32')
|
||||
? absoluteFile?.replaceAll('\\', '/')
|
||||
: absoluteFile
|
||||
|
||||
if (posixAbsoluteFile?.startsWith(`${repoRoot}/`)) {
|
||||
return posixAbsoluteFile.replace(`${repoRoot}/`, '')
|
||||
}
|
||||
|
||||
return relativeFile
|
||||
}
|
||||
|
||||
const captureUserInvocationStack = (ErrorConstructor: SpecWindow['Error'], userInvocationStack?: string | false) => {
|
||||
if (!userInvocationStack) {
|
||||
const newErr = new ErrorConstructor('userInvocationStack')
|
||||
@@ -319,7 +334,6 @@ const getSourceDetailsForLine = (projectRoot, line): LineDetail => {
|
||||
|
||||
// WebKit stacks may include an `<unknown>` or `[native code]` location that is not navigable.
|
||||
// We ensure that the absolute path is not set in this case.
|
||||
|
||||
const canBuildAbsolutePath = relativeFile && projectRoot && (
|
||||
!Cypress.isBrowser('webkit') || (relativeFile !== '<unknown>' && relativeFile !== '[native code]')
|
||||
)
|
||||
@@ -467,6 +481,7 @@ export default {
|
||||
replacedStack,
|
||||
getCodeFrame,
|
||||
getCodeFrameFromSource,
|
||||
getRelativePathFromRoot,
|
||||
getSourceStack,
|
||||
getStackLines,
|
||||
getSourceDetailsForFirstLine,
|
||||
|
||||
Reference in New Issue
Block a user