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:
Rachel
2022-09-20 20:52:24 +00:00
committed by GitHub
parent fd941023a2
commit 070b3c9ba5
10 changed files with 120 additions and 17 deletions

View File

@@ -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
}

View File

@@ -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,

View File

@@ -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',

View File

@@ -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)

View File

@@ -98,6 +98,10 @@ export class DataContext {
this.lifecycleManager = new ProjectLifecycleManager(this)
}
get git () {
return this.coreData.currentProjectGitInfo
}
get schema () {
return this._config.schema
}

View File

@@ -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: {

View File

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

View File

@@ -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()
}
}

View File

@@ -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', () => {

View File

@@ -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,