diff --git a/.eslintrc.js b/.eslintrc.js
index 852411053239..7abb027d019b 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -5,6 +5,7 @@ const { specifiedRules } = require('graphql')
const graphqlOpts = {
env: 'literal',
tagName: 'gql',
+ // eslint-disable-next-line no-restricted-syntax
schemaString: fs.readFileSync(
path.join(__dirname, 'packages/graphql/schemas/schema.graphql'),
'utf8',
@@ -33,6 +34,24 @@ module.exports = {
'plugin:@cypress/dev/tests',
],
parser: '@typescript-eslint/parser',
+ overrides: [
+ {
+ files: [
+ // ignore in tests and scripts
+ '**/scripts/**',
+ '**/test/**',
+ '**/system-tests/**',
+ 'packages/{app,driver,frontend-shared,launchpad}/cypress/**',
+ '*.test.ts',
+ // ignore in packages that don't run in the Cypress process
+ 'npm/create-cypress-tests/**',
+ ],
+ rules: {
+ 'no-restricted-properties': 'off',
+ 'no-restricted-syntax': 'off',
+ },
+ },
+ ],
rules: {
'no-duplicate-imports': 'off',
'import/no-duplicates': 'off',
@@ -55,6 +74,16 @@ module.exports = {
message: 'os.userInfo() will throw when there is not an `/etc/passwd` entry for the current user (like when running with --user 12345 in Docker). Do not use it unless you catch any potential errors.',
},
],
+ 'no-restricted-syntax': [
+ // esquery tool: https://estools.github.io/esquery/
+ 'error',
+ {
+ // match sync FS methods except for `existsSync`
+ // examples: fse.readFileSync, fs.readFileSync, this.ctx.fs.readFileSync...
+ selector: `MemberExpression[object.name='fs'][property.name=/^[A-z]+Sync$/]:not(MemberExpression[property.name='existsSync']), MemberExpression[property.name=/^[A-z]+Sync$/]:not(MemberExpression[property.name='existsSync']):has(MemberExpression[property.name='fs'])`,
+ message: 'Synchronous fs calls should not be used in Cypress. Use an async API instead.',
+ },
+ ],
'graphql/capitalized-type-name': ['warn', graphqlOpts],
'graphql/no-deprecated-fields': ['error', graphqlOpts],
'graphql/template-strings': ['error', { ...graphqlOpts, validators }],
diff --git a/circle.yml b/circle.yml
index 0241aa54efaf..ede5ccf5b0fc 100644
--- a/circle.yml
+++ b/circle.yml
@@ -27,8 +27,7 @@ mainBuildFilters: &mainBuildFilters
branches:
only:
- develop
- - 10.0-release
- - linux-arm64
+ - issue-22147-nohoist
# usually we don't build Mac app - it takes a long time
# but sometimes we want to really confirm we are doing the right thing
@@ -129,7 +128,7 @@ commands:
- run:
name: Check current branch to persist artifacts
command: |
- if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "linux-arm64" ]]; then
+ if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "issue-22147-nohoist" ]]; then
echo "Not uploading artifacts or posting install comment for this branch."
circleci-agent step halt
fi
diff --git a/cli/scripts/build.js b/cli/scripts/build.js
index 7ce6b3d9a8d5..b5fe4fb8c24d 100644
--- a/cli/scripts/build.js
+++ b/cli/scripts/build.js
@@ -31,6 +31,7 @@ function preparePackageForNpmRelease (json) {
delete json['private']
// no need to include "nyc" code coverage settings
delete json.nyc
+ delete json.workspaces
_.extend(json, {
version,
diff --git a/npm/eslint-plugin-dev/lib/custom-rules/index.js b/npm/eslint-plugin-dev/lib/custom-rules/index.js
index 4acf20ebcac0..8dbc761bc096 100644
--- a/npm/eslint-plugin-dev/lib/custom-rules/index.js
+++ b/npm/eslint-plugin-dev/lib/custom-rules/index.js
@@ -2,6 +2,7 @@ const fs = require('fs')
const path = require('path')
module.exports =
+ // eslint-disable-next-line no-restricted-syntax
Object.assign({}, ...fs.readdirSync(__dirname)
.filter((filename) => filename.endsWith('.js') && filename !== 'index.js')
.map((filename) => ({ [filename.replace(/\.js$/u, '')]: require(path.resolve(__dirname, filename)) })))
diff --git a/npm/vite-dev-server/cypress/e2e/vite-dev-server.cy.ts b/npm/vite-dev-server/cypress/e2e/vite-dev-server.cy.ts
index 12d59f6da467..f0dac35ea855 100644
--- a/npm/vite-dev-server/cypress/e2e/vite-dev-server.cy.ts
+++ b/npm/vite-dev-server/cypress/e2e/vite-dev-server.cy.ts
@@ -38,7 +38,7 @@ describe('Config options', () => {
cy.startAppServer('component')
cy.withCtx(async (ctx, { specWithWhitespace }) => {
- ctx.actions.file.writeFileInProject(
+ await ctx.actions.file.writeFileInProject(
ctx.path.join('src', specWithWhitespace),
await ctx.file.readFileInProject(ctx.path.join('src', 'App.cy.jsx')),
)
diff --git a/npm/vite-dev-server/src/plugins/cypress.ts b/npm/vite-dev-server/src/plugins/cypress.ts
index 1e6fdd5a46b1..4ba23ad3aebc 100644
--- a/npm/vite-dev-server/src/plugins/cypress.ts
+++ b/npm/vite-dev-server/src/plugins/cypress.ts
@@ -38,6 +38,8 @@ export const Cypress = (
const indexHtmlFile = options.cypressConfig.indexHtmlFile
let specsPathsSet = getSpecsPathsSet(specs)
+ // TODO: use async fs methods here
+ // eslint-disable-next-line no-restricted-syntax
let loader = fs.readFileSync(INIT_FILEPATH, 'utf8')
devServerEvents.on('dev-server:specs:changed', (specs: Spec[]) => {
diff --git a/npm/webpack-dev-server/src/CypressCTWebpackPlugin.ts b/npm/webpack-dev-server/src/CypressCTWebpackPlugin.ts
index f625d6d2c98a..8cb128b2bbca 100644
--- a/npm/webpack-dev-server/src/CypressCTWebpackPlugin.ts
+++ b/npm/webpack-dev-server/src/CypressCTWebpackPlugin.ts
@@ -99,13 +99,15 @@ export class CypressCTWebpackPlugin {
* has been "updated on disk", causing a recompliation (and pulling the new specs in as
* dependencies).
*/
- private onSpecsChange = (specs: Cypress.Cypress['spec'][]) => {
+ private onSpecsChange = async (specs: Cypress.Cypress['spec'][]) => {
if (!this.compilation || _.isEqual(specs, this.files)) {
return
}
this.files = specs
const inputFileSystem = this.compilation.inputFileSystem
+ // TODO: don't use a sync fs method here
+ // eslint-disable-next-line no-restricted-syntax
const utimesSync: UtimesSync = inputFileSystem.fileSystem.utimesSync ?? fs.utimesSync
utimesSync(path.resolve(__dirname, 'browser.js'), new Date(), new Date())
diff --git a/npm/webpack-dev-server/src/helpers/nextHandler.ts b/npm/webpack-dev-server/src/helpers/nextHandler.ts
index 5f73a7ae1b61..ea6fa62bd9da 100644
--- a/npm/webpack-dev-server/src/helpers/nextHandler.ts
+++ b/npm/webpack-dev-server/src/helpers/nextHandler.ts
@@ -69,7 +69,7 @@ async function loadWebpackConfig (devServerConfig: WebpackDevServerConfig): Prom
buildId: `@cypress/react-${Math.random().toString()}`,
config: nextConfig,
dev: true,
- pagesDir: findPagesDir(devServerConfig.cypressConfig.projectRoot),
+ pagesDir: await findPagesDir(devServerConfig.cypressConfig.projectRoot),
entrypoints: {},
rewrites: { fallback: [], afterFiles: [], beforeFiles: [] },
...runWebpackSpan,
@@ -106,9 +106,9 @@ function checkSWC (
return false
}
-const existsSync = (file: string) => {
+const exists = async (file: string) => {
try {
- fs.accessSync(file, fs.constants.F_OK)
+ await fs.promises.access(file, fs.constants.F_OK)
return true
} catch (_) {
@@ -121,16 +121,16 @@ const existsSync = (file: string) => {
* `${projectRoot}/pages` or `${projectRoot}/src/pages`.
* If neither is found, return projectRoot
*/
-function findPagesDir (projectRoot: string) {
+async function findPagesDir (projectRoot: string) {
// prioritize ./pages over ./src/pages
let pagesDir = path.join(projectRoot, 'pages')
- if (existsSync(pagesDir)) {
+ if (await exists(pagesDir)) {
return pagesDir
}
pagesDir = path.join(projectRoot, 'src', 'pages')
- if (existsSync(pagesDir)) {
+ if (await exists(pagesDir)) {
return pagesDir
}
diff --git a/npm/webpack-dev-server/src/measureWebpackPerformance.ts b/npm/webpack-dev-server/src/measureWebpackPerformance.ts
index e3630c8104c6..39f871fb2641 100644
--- a/npm/webpack-dev-server/src/measureWebpackPerformance.ts
+++ b/npm/webpack-dev-server/src/measureWebpackPerformance.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-restricted-syntax */
/* eslint-disable no-console */
import path from 'path'
import fs from 'fs'
diff --git a/npm/webpack-preprocessor/package.json b/npm/webpack-preprocessor/package.json
index 166861106648..465ad7db54a5 100644
--- a/npm/webpack-preprocessor/package.json
+++ b/npm/webpack-preprocessor/package.json
@@ -11,7 +11,7 @@
"secure": "nsp check",
"semantic-release": "semantic-release",
"size": "npm pack --dry",
- "test": "node ./test-webpack-5.js",
+ "test": "node ./scripts/test-webpack-5.js",
"test-debug": "node --inspect --debug-brk ./node_modules/.bin/_mocha",
"test-e2e": "mocha test/e2e/*.spec.*",
"test-unit": "mocha test/unit/*.spec.*",
diff --git a/npm/webpack-preprocessor/test-webpack-5.js b/npm/webpack-preprocessor/scripts/test-webpack-5.js
similarity index 87%
rename from npm/webpack-preprocessor/test-webpack-5.js
rename to npm/webpack-preprocessor/scripts/test-webpack-5.js
index 4e85c6489303..bb0945ee56a2 100644
--- a/npm/webpack-preprocessor/test-webpack-5.js
+++ b/npm/webpack-preprocessor/scripts/test-webpack-5.js
@@ -1,7 +1,10 @@
const execa = require('execa')
-const pkg = require('./package.json')
+const path = require('path')
+const pkg = require('../package.json')
const fs = require('fs')
+const pkgJsonPath = path.join(__dirname, '..', 'package.json')
+
/**
* This file installs Webpack 5 and runs the unit and e2e tests for the preprocessor.
* We read package.json, update the webpack version, then re-run yarn install.
@@ -17,7 +20,7 @@ const main = async () => {
const install = () => execa('yarn', ['install', '--ignore-scripts'], { stdio: 'inherit' })
const resetPkg = async () => {
- fs.writeFileSync('package.json', originalPkg, 'utf8')
+ fs.writeFileSync(pkgJsonPath, originalPkg, 'utf8')
await install()
}
@@ -38,7 +41,7 @@ const main = async () => {
delete pkg.devDependencies['webpack']
// eslint-disable-next-line no-console
console.log('[@cypress/webpack-preprocessor]: updating package.json...')
- fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2))
+ fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2))
// eslint-disable-next-line no-console
console.log('[@cypress/webpack-preprocessor]: install dependencies...')
diff --git a/packages/app/cypress/e2e/cypress-in-cypress-component.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress-component.cy.ts
index ee3110f508d9..d1b480379a04 100644
--- a/packages/app/cypress/e2e/cypress-in-cypress-component.cy.ts
+++ b/packages/app/cypress/e2e/cypress-in-cypress-component.cy.ts
@@ -208,8 +208,8 @@ describe('Cypress In Cypress CT', { viewportWidth: 1500, defaultCommandTimeout:
}).invoke('connected').should('be.true')
})
- cy.withCtx((ctx, o) => {
- ctx.actions.file.writeFileInProject(o.path, `
+ cy.withCtx(async (ctx, o) => {
+ await ctx.actions.file.writeFileInProject(o.path, `
import React from 'react'
import { mount } from '@cypress/react'
diff --git a/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts
index 4cf9c55fffec..7da4e7badfcd 100644
--- a/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts
+++ b/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts
@@ -259,8 +259,8 @@ describe('Cypress In Cypress E2E', { viewportWidth: 1500, defaultCommandTimeout:
}).invoke('connected').should('be.true')
})
- cy.withCtx((ctx, o) => {
- ctx.actions.file.writeFileInProject(o.path, `
+ cy.withCtx(async (ctx, o) => {
+ await ctx.actions.file.writeFileInProject(o.path, `
describe('Dom Content', () => {
it('renders the new test content', () => {
cy.visit('cypress/e2e/dom-content.html')
diff --git a/packages/app/cypress/e2e/cypress-in-cypress.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress.cy.ts
index b9af2b6e1db9..b50001331544 100644
--- a/packages/app/cypress/e2e/cypress-in-cypress.cy.ts
+++ b/packages/app/cypress/e2e/cypress-in-cypress.cy.ts
@@ -228,13 +228,13 @@ describe('Cypress in Cypress', { viewportWidth: 1500, defaultCommandTimeout: 100
startAtSpecsPage('e2e')
cy.get('[data-cy="spec-item"]')
- cy.withCtx((ctx, o) => {
+ cy.withCtx(async (ctx, o) => {
ctx.coreData.app.browserStatus = 'open'
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(`e2e: {`, `e2e: {\n chromeWebSecurity: false,\n`)
- ctx.actions.file.writeFileInProject('cypress.config.js', config)
+ await ctx.actions.file.writeFileInProject('cypress.config.js', config)
o.sinon.stub(ctx.actions.browser, 'closeBrowser')
o.sinon.stub(ctx.actions.browser, 'relaunchBrowser')
@@ -252,24 +252,24 @@ describe('Cypress in Cypress', { viewportWidth: 1500, defaultCommandTimeout: 100
it('restarts browser if there is a before:browser:launch task and there is a change on the config', () => {
startAtSpecsPage('e2e')
- cy.withCtx((ctx, o) => {
+ cy.withCtx(async (ctx, o) => {
ctx.coreData.app.browserStatus = 'open'
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(`e2e: {`, `e2e: {\n setupNodeEvents(on) {\n on('before:browser:launch', () => {})\n},\n`)
- ctx.actions.file.writeFileInProject('cypress.config.js', config)
+ await ctx.actions.file.writeFileInProject('cypress.config.js', config)
})
cy.get('[data-cy="spec-item"]')
- cy.withCtx((ctx, o) => {
+ cy.withCtx(async (ctx, o) => {
ctx.coreData.app.browserStatus = 'open'
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(`e2e: {`, `e2e: {\n viewportHeight: 600,\n`)
- ctx.actions.file.writeFileInProject('cypress.config.js', config)
+ await ctx.actions.file.writeFileInProject('cypress.config.js', config)
o.sinon.stub(ctx.actions.browser, 'closeBrowser')
o.sinon.stub(ctx.actions.browser, 'relaunchBrowser')
@@ -288,14 +288,14 @@ describe('Cypress in Cypress', { viewportWidth: 1500, defaultCommandTimeout: 100
startAtSpecsPage('e2e')
cy.get('[data-cy="spec-item"]')
- cy.withCtx((ctx, o) => {
+ cy.withCtx(async (ctx, o) => {
ctx.coreData.app.browserStatus = 'open'
o.sinon.stub(ctx.actions.project, 'initializeActiveProject')
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(`{`, `{\n watchForFileChanges: false,\n`)
- ctx.actions.file.writeFileInProject('cypress.config.js', config)
+ await ctx.actions.file.writeFileInProject('cypress.config.js', config)
})
cy.get('[data-cy="loading-spinner"]').should('be.visible')
@@ -310,14 +310,14 @@ describe('Cypress in Cypress', { viewportWidth: 1500, defaultCommandTimeout: 100
startAtSpecsPage('e2e')
cy.get('[data-cy="spec-item"]')
- cy.withCtx((ctx, o) => {
+ cy.withCtx(async (ctx, o) => {
ctx.coreData.app.browserStatus = 'open'
o.sinon.stub(ctx.actions.project, 'initializeActiveProject')
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(` e2e: {`, ` e2e: {\n baseUrl: 'https://example.cypress.io',\n`)
- ctx.actions.file.writeFileInProject('cypress.config.js', config)
+ await ctx.actions.file.writeFileInProject('cypress.config.js', config)
})
cy.get('[data-cy="loading-spinner"]').should('be.visible')
diff --git a/packages/app/cypress/e2e/specs.cy.ts b/packages/app/cypress/e2e/specs.cy.ts
index 062eda47086e..1716658ce2e4 100644
--- a/packages/app/cypress/e2e/specs.cy.ts
+++ b/packages/app/cypress/e2e/specs.cy.ts
@@ -423,7 +423,7 @@ describe('App: Specs', () => {
it('generates spec with file name that does not contain a known spec extension', () => {
cy.withCtx(async (ctx) => {
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(
`specPattern: 'src/**/*.{cy,spec}.{js,jsx}'`,
@@ -700,7 +700,7 @@ describe('App: Specs', () => {
it('generates spec with file name that does not contain a known spec extension', () => {
cy.withCtx(async (ctx) => {
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(
`specPattern: 'src/specs-folder/*.cy.{js,jsx}'`,
diff --git a/packages/app/cypress/e2e/subscriptions/configChange-subscription.cy.ts b/packages/app/cypress/e2e/subscriptions/configChange-subscription.cy.ts
index 0e91b3a7f9f9..c2215175c2cc 100644
--- a/packages/app/cypress/e2e/subscriptions/configChange-subscription.cy.ts
+++ b/packages/app/cypress/e2e/subscriptions/configChange-subscription.cy.ts
@@ -1,18 +1,18 @@
function updateProjectIdInCypressConfig (value: string) {
- return cy.withCtx((ctx, o) => {
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ return cy.withCtx(async (ctx, o) => {
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(`projectId: 'abc123'`, `projectId: '${o.value}'`)
- ctx.actions.file.writeFileInProject('cypress.config.js', config)
+ await ctx.actions.file.writeFileInProject('cypress.config.js', config)
}, { value })
}
function updateViewportHeightInCypressConfig (value: number) {
- return cy.withCtx((ctx, o) => {
- let config = ctx.actions.file.readFileInProject('cypress.config.js')
+ return cy.withCtx(async (ctx, o) => {
+ let config = await ctx.actions.file.readFileInProject('cypress.config.js')
config = config.replace(`e2e: {`, `e2e: {\n viewportHeight: ${o.value},\n`)
- ctx.actions.file.writeFileInProject('cypress.config.js', config)
+ await ctx.actions.file.writeFileInProject('cypress.config.js', config)
}, { value })
}
diff --git a/packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts b/packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts
index 9f5f9b968687..95e5a37f3b61 100644
--- a/packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts
+++ b/packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts
@@ -18,8 +18,8 @@ describe('specChange subscription', () => {
.should('contain', 'dom-content.spec.js')
.should('contain', 'dom-list.spec.js')
- cy.withCtx((ctx, o) => {
- ctx.actions.file.writeFileInProject(o.path, '')
+ cy.withCtx(async (ctx, o) => {
+ await ctx.actions.file.writeFileInProject(o.path, '')
}, { path: getPathForPlatform('cypress/e2e/new-file.spec.js') })
cy.get('[data-cy="spec-item-link"]')
diff --git a/packages/app/src/specs/CreateSpecModal.cy.tsx b/packages/app/src/specs/CreateSpecModal.cy.tsx
index ea0b567c83ae..2b79f7108c3f 100644
--- a/packages/app/src/specs/CreateSpecModal.cy.tsx
+++ b/packages/app/src/specs/CreateSpecModal.cy.tsx
@@ -64,6 +64,94 @@ describe('