Files
cypress/packages/server/lib/saved_state.ts
T
Lachlan Miller cc3be10f73 feat(graphql): ability to update/query for appData (#19082)
* move to ts

* refactor

* test

* type

* fix tests

* types

* simplify update preferences via graphql

* types

* simplify

* show config in editor

* fix test

* add description for mutation and update default prefernces

* update schema

* update docs

* update description of localSettings field

Co-authored-by: Mark Noonan <mark@cypress.io>
Co-authored-by: Mark Noonan <oddlyaromatic@gmail.com>
2021-11-29 23:04:57 +10:00

125 lines
3.1 KiB
TypeScript

import _ from 'lodash'
import path from 'path'
import Debug from 'debug'
import Bluebird from 'bluebird'
import appData from './util/app_data'
import cwd from './cwd'
import FileUtil from './util/file'
import { fs } from './util/fs'
import { AllowedState, allowedKeys } from '@packages/types'
const debug = Debug('cypress:server:saved_state')
const stateFiles: Record<string, typeof FileUtil> = {}
// TODO: remove `showedOnBoardingModal` from this list - it is only included so that misleading `allowed` are not thrown
// now that it has been removed from use
export const formStatePath = (projectRoot?: string) => {
return Bluebird.try(() => {
debug('making saved state from %s', cwd())
if (projectRoot) {
debug('for project path %s', projectRoot)
return projectRoot
}
debug('missing project path, looking for project here')
let cypressConfigPath = cwd('cypress.config.js')
return fs.pathExistsAsync(cypressConfigPath)
.then((found) => {
if (found) {
debug('found cypress file %s', cypressConfigPath)
projectRoot = cwd()
return
}
cypressConfigPath = cwd('cypress.config.ts')
return fs.pathExistsAsync(cypressConfigPath)
})
.then((found) => {
if (found) {
debug('found cypress file %s', cypressConfigPath)
projectRoot = cwd()
}
return projectRoot
})
}).then((projectRoot) => {
const fileName = 'state.json'
if (projectRoot) {
debug(`state path for project ${projectRoot}`)
return path.join(appData.toHashName(projectRoot), fileName)
}
debug('state path for global mode')
return path.join('__global__', fileName)
})
}
const normalizeAndAllowSet = (set, key, value) => {
const valueObject = (() => {
if (_.isString(key)) {
const tmp = {}
tmp[key] = value
return tmp
}
return key
})()
const invalidKeys = _.filter(_.keys(valueObject), (key) => {
return !_.includes(allowedKeys, key)
})
if (invalidKeys.length) {
// eslint-disable-next-line no-console
console.error(`WARNING: attempted to save state for non-allowed key(s): ${invalidKeys.join(', ')}. All keys must be allowed in server/lib/saved_state.js`)
}
return set(_.pick(valueObject, allowedKeys))
}
interface SavedStateAPI {
get: () => Bluebird<AllowedState>
set: (stateToSet: AllowedState) => Bluebird<void>
}
export const create = (projectRoot?: string, isTextTerminal: boolean = false): Bluebird<SavedStateAPI> => {
if (isTextTerminal) {
debug('noop saved state')
return Bluebird.resolve(FileUtil.noopFile)
}
return formStatePath(projectRoot)
.then((statePath: string) => {
const fullStatePath = appData.projectsPath(statePath)
debug('full state path %s', fullStatePath)
if (stateFiles[fullStatePath]) {
return stateFiles[fullStatePath]
}
debug('making new state file around %s', fullStatePath)
const stateFile = new FileUtil({
path: fullStatePath,
})
stateFile.set = _.wrap(stateFile.set.bind(stateFile), normalizeAndAllowSet)
stateFiles[fullStatePath] = stateFile
return stateFile as SavedStateAPI
})
}