mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-27 17:39:10 -06:00
* dependency: Replace jsonlint with json-parse-even-better-errors * run on windows * add changelog entry * Update changelog entry * Update changelog entry * yarn lock * changelog update + lock
216 lines
5.3 KiB
JavaScript
216 lines
5.3 KiB
JavaScript
const path = require('path')
|
|
const check = require('syntax-error')
|
|
const debug = require('debug')('cypress:server:fixture')
|
|
const coffee = require('coffeescript')
|
|
const Promise = require('bluebird')
|
|
const jsonParseBetterErrors = require('json-parse-even-better-errors')
|
|
const stripAnsi = require('strip-ansi')
|
|
|
|
const errors = require('./errors')
|
|
const { fs } = require('./util/fs')
|
|
const glob = require('./util/glob')
|
|
|
|
const extensions = [
|
|
'.json',
|
|
'.js',
|
|
'.coffee',
|
|
'.html',
|
|
'.txt',
|
|
'.csv',
|
|
'.png',
|
|
'.jpg',
|
|
'.jpeg',
|
|
'.gif',
|
|
'.tif',
|
|
'.tiff',
|
|
'.zip',
|
|
]
|
|
|
|
const queue = {}
|
|
|
|
const friendlyJsonParse = function (s) {
|
|
jsonParseBetterErrors(s) // should throw an error with better formatting
|
|
|
|
return JSON.parse(s) // actually parses correctly all the edge cases
|
|
}
|
|
|
|
module.exports = {
|
|
get (fixturesFolder, filePath, options = {}) {
|
|
const p = path.join(fixturesFolder, filePath)
|
|
const fixture = path.basename(p)
|
|
|
|
// if the file exists, go ahead and parse it
|
|
// otherwise, glob for potential extensions
|
|
return this.fileExists(p)
|
|
.then(function () {
|
|
debug('fixture exact name exists', p)
|
|
|
|
return this.parseFile(p, fixture, options)
|
|
}).catch(function (e) {
|
|
if (e.code !== 'ENOENT') {
|
|
throw e
|
|
}
|
|
|
|
const pattern = `${p}{${extensions.join(',')}}`
|
|
|
|
return glob(pattern, {
|
|
nosort: true,
|
|
nodir: true,
|
|
})
|
|
.bind(this)
|
|
.then(function (matches) {
|
|
if (matches.length === 0) {
|
|
const relativePath = path.relative('.', p)
|
|
|
|
// TODO: there's no reason this error should be in
|
|
// the @packages/error list, it should be written in
|
|
// the driver since this error can only occur within
|
|
// driver commands and not outside of the test runner
|
|
const err = errors.get('FIXTURE_NOT_FOUND', relativePath, extensions)
|
|
|
|
err.message = stripAnsi(err.message)
|
|
throw err
|
|
}
|
|
|
|
debug('fixture matches found, using the first', matches)
|
|
|
|
const ext = path.extname(matches[0])
|
|
|
|
return this.parseFile(p + ext, fixture, options)
|
|
})
|
|
})
|
|
},
|
|
|
|
fileExists (p) {
|
|
return fs.statAsync(p).bind(this)
|
|
.then((stat) => {
|
|
// check for files, not directories
|
|
// https://github.com/cypress-io/cypress/issues/3739
|
|
if (stat.isDirectory()) {
|
|
const err = new Error()
|
|
|
|
err.code = 'ENOENT'
|
|
throw err
|
|
}
|
|
})
|
|
},
|
|
|
|
parseFile (p, fixture, options) {
|
|
if (queue[p]) {
|
|
return Promise.delay(1).then(() => {
|
|
return this.parseFile(p, fixture, options)
|
|
})
|
|
}
|
|
|
|
queue[p] = true
|
|
|
|
const cleanup = () => {
|
|
return delete queue[p]
|
|
}
|
|
|
|
return this.fileExists(p)
|
|
.then(function () {
|
|
const ext = path.extname(p)
|
|
|
|
return this.parseFileByExtension(p, fixture, ext, options)
|
|
}).then((ret) => {
|
|
cleanup()
|
|
|
|
return ret
|
|
}).catch((err) => {
|
|
cleanup()
|
|
|
|
throw err
|
|
})
|
|
},
|
|
|
|
parseFileByExtension (p, fixture, ext, options = {}) {
|
|
// https://github.com/cypress-io/cypress/issues/1558
|
|
// If the user explicitly specifies `null` as the encoding, we treat the
|
|
// file as binary regardless of extension. We base64 encode them for
|
|
// transmission over the websocket. There is a matching Buffer.from()
|
|
// in packages/driver/src/cy/commands/fixtures.ts
|
|
if (options.encoding === null) {
|
|
return this.parse(p, fixture)
|
|
}
|
|
|
|
switch (ext) {
|
|
case '.json': return this.parseJson(p, fixture)
|
|
case '.js': return this.parseJs(p, fixture)
|
|
case '.coffee': return this.parseCoffee(p, fixture)
|
|
case '.html': return this.parseHtml(p, fixture)
|
|
case '.png': case '.jpg': case '.jpeg': case '.gif': case '.tif': case '.tiff': case '.zip':
|
|
return this.parse(p, fixture, options.encoding)
|
|
default:
|
|
return this.parse(p, fixture, options.encoding || 'utf8')
|
|
}
|
|
},
|
|
|
|
parseJson (p, fixture) {
|
|
return fs.readFileAsync(p, 'utf8')
|
|
.bind(this)
|
|
.then(friendlyJsonParse)
|
|
.catch((err) => {
|
|
throw new Error(`'${fixture}' is not valid JSON.\n${err.message}`)
|
|
})
|
|
},
|
|
|
|
parseJs (p, fixture) {
|
|
return fs.readFileAsync(p, 'utf8')
|
|
.bind(this)
|
|
.then((str) => {
|
|
let obj
|
|
|
|
try {
|
|
obj = eval(`(${str})`)
|
|
} catch (e) {
|
|
const err = check(str, fixture)
|
|
|
|
if (err) {
|
|
throw err
|
|
}
|
|
|
|
throw e
|
|
}
|
|
|
|
return obj
|
|
}).catch((err) => {
|
|
throw new Error(`'${fixture}' is not a valid JavaScript object.\n${err.toString()}`)
|
|
})
|
|
},
|
|
|
|
parseCoffee (p, fixture) {
|
|
const dc = process.env.NODE_DISABLE_COLORS
|
|
|
|
process.env.NODE_DISABLE_COLORS = '0'
|
|
|
|
return fs.readFileAsync(p, 'utf8')
|
|
.bind(this)
|
|
.then((str) => {
|
|
str = coffee.compile(str, { bare: true })
|
|
|
|
return eval(str)
|
|
}).catch((err) => {
|
|
throw new Error(`'${fixture} is not a valid CoffeeScript object.\n${err.toString()}`)
|
|
}).finally(() => {
|
|
return process.env.NODE_DISABLE_COLORS = dc
|
|
})
|
|
},
|
|
|
|
parseHtml (p, fixture) {
|
|
return fs.readFileAsync(p, 'utf8')
|
|
.bind(this)
|
|
.catch((err) => {
|
|
throw new Error(`Unable to parse '${fixture}'.\n${err.toString()}`)
|
|
})
|
|
},
|
|
|
|
parse (p, fixture, encoding) {
|
|
return fs.readFileAsync(p, encoding)
|
|
.bind(this)
|
|
.catch((err) => {
|
|
throw new Error(`Unable to parse '${fixture}'.\n${err.toString()}`)
|
|
})
|
|
},
|
|
}
|