Issue 118 - scaffolding in /tmp folder (#128)

* server: detect symlinked case in support file search for #118

* server fix problem found by unit test

* add unit test for paths

* correctly determining files in /tmp for a project

* server: cleanup resolving symlinks
This commit is contained in:
Gleb Bahmutov
2017-06-06 10:03:49 -04:00
committed by GitHub
parent afcf9522d2
commit ba87bd5dfe
6 changed files with 108 additions and 29 deletions

View File

@@ -1,6 +1,7 @@
_ = require("lodash")
path = require("path")
Promise = require("bluebird")
fs = require("fs")
errors = require("./errors")
scaffold = require("./scaffold")
errors = require("./errors")
@@ -8,6 +9,8 @@ origin = require("./util/origin")
coerce = require("./util/coerce")
settings = require("./util/settings")
v = require("./util/validation")
log = require("./log")
pathHelpers = require("./util/path_helpers")
## cypress following by _
cypressEnvRe = /^(cypress_)/i
@@ -233,26 +236,42 @@ module.exports = {
return obj
setSupportFileAndFolder: (obj) ->
return obj if not obj.supportFile
obj = _.clone(obj)
## if supportFile isn't false
if sf = obj.supportFile
try
## resolve full path with extension to
obj.supportFile = require.resolve(sf)
catch err
## supportFile doesn't exist on disk
if sf isnt path.resolve(obj.projectRoot, defaults.supportFile)
## throw because they have it explicitly set,
## so it should be there
errors.throw("SUPPORT_FILE_NOT_FOUND", path.resolve(obj.projectRoot, sf))
else
## set it to support/index.js, and it will be scaffolded
## later in process
obj.supportFile = path.join(sf, "index.js")
## TODO move this logic to find support file into util/path_helpers
## set config.supportFolder to its directory
obj.supportFolder = path.dirname(obj.supportFile)
sf = obj.supportFile
log "setting support file #{sf}"
log "for project root #{obj.projectRoot}"
try
## resolve full path with extension
obj.supportFile = require.resolve(sf)
if pathHelpers.checkIfResolveChangedRootFolder(obj.supportFile, sf)
log("require.resolve switched support folder from %s to %s",
sf, obj.supportFile)
# this means the path was probably symlinked, like
# /tmp/foo -> /private/tmp/foo
# which can confuse the rest of the code
# switch it back to "normal" file
obj.supportFile = path.join(sf, path.basename(obj.supportFile))
if not fs.existsSync(obj.supportFile)
errors.throw("SUPPORT_FILE_NOT_FOUND", obj.supportFile)
log("switching to found file %s", obj.supportFile)
catch err
## supportFile doesn't exist on disk
if sf isnt path.resolve(obj.projectRoot, defaults.supportFile)
## throw because they have it explicitly set,
## so it should be there
errors.throw("SUPPORT_FILE_NOT_FOUND", path.resolve(obj.projectRoot, sf))
else
## set it to support/index.js, and it will be scaffolded
## later in process
obj.supportFile = path.join(sf, "index.js")
## set config.supportFolder to its directory
obj.supportFolder = path.dirname(obj.supportFile)
log "set support folder #{obj.supportFolder}"
return obj

View File

@@ -9,6 +9,7 @@ user = require("../user")
pathHelpers = require("../util/path_helpers")
CacheBuster = require("../util/cache_buster")
errors = require("../errors")
log = require("debug")("cypress:server:files")
glob = Promise.promisify(glob)
@@ -102,6 +103,8 @@ module.exports = {
getTestFiles: (config) ->
integrationFolderPath = config.integrationFolder
log("looking for test files in the integration folder %s",
integrationFolderPath)
## support files are not automatically
## ignored because only _fixtures are hard
@@ -126,7 +129,7 @@ module.exports = {
## ignore fixtures + javascripts
options = {
sort: true
realpath: true
absolute: true
cwd: integrationFolderPath
ignore: [].concat(
javascriptsPath,
@@ -145,7 +148,15 @@ module.exports = {
relativePathFromProjectRoot = (file) ->
path.relative(config.projectRoot, file)
setNameParts = (file) ->
log("found test file %s", file)
{
name: relativePathFromIntegrationFolder(file)
path: relativePathFromProjectRoot(file)
absolute: file
}
ignorePatterns = [].concat(config.ignoreTestFiles)
## a function which returns true if the file does NOT match
@@ -164,12 +175,7 @@ module.exports = {
## filter out anything that matches our
## ignored test files glob
.filter(doesNotMatchAllIgnoredPatterns)
.map (file) ->
{
name: relativePathFromIntegrationFolder(file)
path: relativePathFromProjectRoot(file)
absolute: file
}
.map(setNameParts)
.then (arr) ->
{
integration: arr

View File

@@ -168,6 +168,7 @@ module.exports = {
getFilePath = (dir, name) ->
path.relative(config.projectRoot, path.join(dir, name))
log("example spec from integration folder %s", config.integrationFolder)
files = [
getFilePath(config.integrationFolder, "example_spec.js")
]
@@ -178,11 +179,13 @@ module.exports = {
])
if config.supportFolder and config.supportFile isnt false
log "supporting files from folder #{config.supportFolder}"
files = files.concat([
getFilePath(config.supportFolder, "commands.js")
getFilePath(config.supportFolder, "defaults.js")
getFilePath(config.supportFolder, "index.js")
])
log("scaffolded files %j", files)
return @_fileListToTree(files)

View File

@@ -1,9 +1,43 @@
fs = require("fs")
path = require("path")
Promise = require("bluebird")
fs = Promise.promisifyAll(fs)
isIntegrationTestRe = /^integration/
isUnitTestRe = /^unit/
# require.resolve walks the symlinks, which can really change
# the results. For example
# /tmp/foo is symlink to /private/tmp/foo on Mac
# thus resolving /tmp/foo to find /tmp/foo/index.js
# can return /private/tmp/foo/index.js
# which can really confuse the rest of the code.
# Detect this switch by checking if the resolution of absolute
# paths moved the prefix
#
# Good case: no switcheroo, return false
# /foo/bar -> /foo/bar/index.js
# Bad case: return true
# /tmp/foo/bar -> /private/tmp/foo/bar/index.js
checkIfResolveChangedRootFolder = (resolved, initial) ->
path.isAbsolute(resolved) &&
path.isAbsolute(initial) &&
!resolved.startsWith(initial)
# real folder path found could be different due to symlinks
# For example, folder /tmp/foo on Mac is really /private/tmp/foo
getRealFolderPath = (folder) ->
# TODO check if folder is a non-empty string
throw new Error("Expected folder") if not folder
fs.realpathAsync(folder)
module.exports = {
checkIfResolveChangedRootFolder
getRealFolderPath
getAbsolutePathToSpec: (spec, config) ->
switch
## if our file is an integration test
@@ -29,4 +63,4 @@ module.exports = {
else
spec
}
}

View File

@@ -1,4 +1,5 @@
log = require('../log')
cwd = require('../cwd')
fs = require('fs')
{ basename, join, isAbsolute } = require('path')
md5 = require('md5')
@@ -12,15 +13,15 @@ toHashName = (projectPath) ->
"#{name}-#{hash}"
formStatePath = (projectPath) ->
log('making saved state from %s', process.cwd())
log('making saved state from %s', cwd())
if projectPath
log('for project path %s', projectPath)
else
log('missing project path, looking for project here')
cypressJsonPath = join(process.cwd(), 'cypress.json')
cypressJsonPath = cwd('cypress.json')
if fs.existsSync(cypressJsonPath)
log('found cypress file %s', cypressJsonPath)
projectPath = process.cwd()
projectPath = cwd()
statePath = "state.json"
if projectPath

View File

@@ -0,0 +1,16 @@
require("../spec_helper")
path_helpers = require("#{root}lib/util/path_helpers")
describe "lib/util/path_helpers", ->
context "checkIfResolveChangedRootFolder", ->
check = path_helpers.checkIfResolveChangedRootFolder
it "ignores non-absolute paths", ->
expect(check('foo/index.js', 'foo')).to.be.false()
it "handles paths that do not switch", ->
expect(check('/foo/index.js', '/foo')).to.be.false()
it "detects path switch", ->
expect(check('/private/foo/index.js', '/foo')).to.be.true()