diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index 161e6be406..4d0f51a036 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -2,7 +2,7 @@ version: 2.1 chrome-stable-version: &chrome-stable-version "138.0.7204.183" chrome-beta-version: &chrome-beta-version "139.0.7258.66" -firefox-stable-version: &firefox-stable-version "137.0" +firefox-stable-version: &firefox-stable-version "141.0" orbs: browser-tools: circleci/browser-tools@2.1.1 diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 3837ebe320..35a5d51746 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -3828,7 +3828,7 @@ declare namespace Cypress { validate?: () => Promise | void } - type SameSiteStatus = 'no_restriction' | 'strict' | 'lax' + type SameSiteStatus = 'no_restriction' | 'strict' | 'lax' | 'unspecified' interface SelectFileOptions extends Loggable, Timeoutable, ActionableOptions { /** @@ -3879,8 +3879,8 @@ declare namespace Cypress { */ expiry: number /** - * The cookie's SameSite value. If set, should be one of `lax`, `strict`, or `no_restriction`. - * `no_restriction` is the equivalent of `SameSite=None`. Pass `undefined` to use the browser's default. + * The cookie's SameSite value. If set, should be one of `lax`, `strict`, `no_restriction`, or `unspecified`. + * `no_restriction` is the equivalent of `SameSite=None`. Pass `undefined` to use the browser's default ('unspecified' is the default for Firefox 140 and up). * Note: `no_restriction` can only be used if the secure flag is set to `true`. * @default undefined */ diff --git a/guides/eslint-migration.md b/guides/eslint-migration.md index ec041e00fe..a433da03ca 100644 --- a/guides/eslint-migration.md +++ b/guides/eslint-migration.md @@ -108,39 +108,50 @@ For each package in the batch: - In each migrated package, add `@packages/eslint-config` to `devDependencies` (use a relative file path if not published to npm). - **Add ESLint as a dev dependency:** - Since `@packages/eslint-config` has ESLint as a peer dependency, add `eslint: "^9.18.0"` to `devDependencies`. -4. **Run lint and autofix:** +4. **Add lint-staged configuration:** + - Add a `lint-staged` section to the package's `package.json`: + ```json + { + "lint-staged": { + "**/*.{js,jsx,ts,tsx}": "eslint --fix" + } + } + ``` + - This ensures that when files are staged for commit, they are automatically linted and fixed using the package's local ESLint configuration. +5. **Run lint and autofix:** - From the package root, run: ``` npx eslint . --ext .js,.ts,.tsx,.jsx --fix ``` - Manually fix any remaining lint errors. -5. **Verify TypeScript configuration:** +6. **Verify TypeScript configuration:** - Ensure the package has a valid `tsconfig.json` that works with the new ESLint config. - Run `npx tsc --noEmit` to check for TypeScript compilation errors. - Verify that the new ESLint config can properly parse TypeScript files in the package. -6. **Run tests for the package** to ensure nothing broke. -7. **Commit changes** with a clear message, e.g.: +7. **Run tests for the package** to ensure nothing broke. +8. **Commit changes** with a clear message, e.g.: ``` chore(npm/grep): migrate to @packages/eslint-config and remove legacy eslint-plugin-dev ``` -### 3. **Open a PR for Each Batch** +### 4. **Open a PR for Each Batch** - Keep each migration PR focused (one batch per PR). - List all affected packages in the PR description. - Include a checklist for each package: - [ ] Removed old ESLint config - [ ] Added new config + - [ ] Added lint-staged configuration - [ ] Ran lint and fixed errors - [ ] Ran tests -### 4. **Document Issues or Gaps** +### 5. **Document Issues or Gaps** - If you hit any missing rules or plugin gaps, note them for follow-up. - If a package needs a custom override, add it in a local `eslint.config.ts` (prefer to upstream to the shared config if possible). -### 5. **Deprecate and Remove Old Plugin** +### 6. **Deprecate and Remove Old Plugin** - Once all packages are migrated, remove `@cypress/eslint-plugin-dev` from the repo and CI. -### 6. **Simplify Lint-Staged Configuration** +### 7. **Simplify Lint-Staged Configuration** After all packages are migrated, simplify the lint-staged configuration in root `package.json`: ```json @@ -152,7 +163,7 @@ After all packages are migrated, simplify the lint-staged configuration in root } ``` -### 7. **Update Lerna/Monorepo Config** +### 8. **Update Lerna/Monorepo Config** - Ensure all packages reference the new config in their `package.json`/`eslint.config.ts`. - Update documentation and developer onboarding guides. @@ -317,6 +328,7 @@ For each package, ensure you've completed: - [ ] Removed `.eslintrc*` files - [ ] Created `eslint.config.ts` with proper configuration - [ ] Added required dependencies (`eslint`, `@packages/eslint-config`, `jiti`) +- [ ] Added lint-staged configuration to `package.json` - [ ] Created/updated `tsconfig.json` that extends base config - [ ] Updated ESLint scripts (removed `--ext` flag) - [ ] Ran `yarn lint` successfully diff --git a/npm/grep/package.json b/npm/grep/package.json index 8836a95f98..cc075e7a40 100644 --- a/npm/grep/package.json +++ b/npm/grep/package.json @@ -40,6 +40,9 @@ "cypress", "grep" ], + "lint-staged": { + "**/*.{js,jsx,ts,tsx,json}": "eslint --fix" + }, "resolutions": { "jiti": "^2.4.2" }, diff --git a/package.json b/package.json index dbd4539609..d32f87a7d5 100644 --- a/package.json +++ b/package.json @@ -265,27 +265,7 @@ ] }, "lint-staged": { - "npm/grep/**/*.{js,jsx,ts,tsx}": "yarn lint:fix", - "*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "cli/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "packages/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "scripts/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "system-tests/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "tooling/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/angular/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/cypress-schematic/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/eslint-plugin-dev/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/mount-utils/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/puppeteer/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/react/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/svelte/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/vite-dev-server/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/vite-plugin-cypress-esm/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/vue/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/webpack-batteries-included-preprocessor/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/webpack-dev-server/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/webpack-preprocessor/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", - "npm/xpath/**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", + "**/*.{js,jsx,ts,tsx,json,eslintrc,vue}": "eslint --fix", "*workflows.yml": "node scripts/format-workflow-file.js" }, "resolutions": { diff --git a/packages/driver/cypress/e2e/commands/cookies.cy.js b/packages/driver/cypress/e2e/commands/cookies.cy.js index 15cbc01643..ca87b1c9d9 100644 --- a/packages/driver/cypress/e2e/commands/cookies.cy.js +++ b/packages/driver/cypress/e2e/commands/cookies.cy.js @@ -1374,10 +1374,17 @@ describe('src/cy/commands/cookies', () => { cy.setCookie('five', 'bar') - // @see https://bugzilla.mozilla.org/show_bug.cgi?id=1624668 - // TODO(webkit): pw webkit has the same issue as firefox (no "unspecified" state), need a patched binary - if (Cypress.isBrowser('firefox') || Cypress.isBrowser('webkit')) { - cy.getCookie('five').should('include', { sameSite: 'no_restriction' }) + // @see https://bugzilla.mozilla.org/show_bug.cgi?id=1550032 + // Firefox bidi returns "unspecified" for sameSite; + // webkit & firefox < 135 return "no_restriction" for sameSite; + // other browsers do not return sameSite at all + const sameSite = ( + Cypress.isBrowser('webkit') + ) ? 'no_restriction' : + Cypress.isBrowser('firefox') ? 'unspecified' : null + + if (sameSite) { + cy.getCookie('five').should('include', { sameSite }) } else { cy.getCookie('five').should('not.have.property', 'sameSite') } @@ -1515,7 +1522,7 @@ describe('src/cy/commands/cookies', () => { assertLogLength(this.logs, 1) expect(lastLog.get('error').message).to.eq(stripIndent` If a \`sameSite\` value is supplied to \`cy.setCookie()\`, it must be a string from the following list: - > no_restriction, lax, strict + > no_restriction, lax, strict, unspecified You passed: > bad`) diff --git a/packages/driver/cypress/e2e/e2e/e2e_cookies.cy.js b/packages/driver/cypress/e2e/e2e/e2e_cookies.cy.js index cd7512b0de..c78f5675c0 100644 --- a/packages/driver/cypress/e2e/e2e/e2e_cookies.cy.js +++ b/packages/driver/cypress/e2e/e2e/e2e_cookies.cy.js @@ -8,7 +8,7 @@ const cleanse = (cookies) => { }) } -const firefoxDefaultSameSite = Cypress.isBrowser({ family: 'firefox' }) ? { sameSite: 'no_restriction' } : {} +const firefoxDefaultSameSite = Cypress.isBrowser({ family: 'firefox' }) ? { sameSite: 'unspecified' } : {} describe('e2e cookies spec', () => { it('simple cookie', () => { diff --git a/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts b/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts index a747a1ec4b..23742ec6e7 100644 --- a/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/cookie_login.cy.ts @@ -192,8 +192,7 @@ describe('cy.origin - cookie login', { browser: '!webkit' }, () => { verifyIdpNotLoggedIn({ expectNullCookie: false }) }) - // FIXME: Currently in Firefox, the default cookie setting in the extension is no_restriction, which can be set with Secure=false. - it('SameSite=None -> not logged in', { browser: '!firefox' }, () => { + it('SameSite=None -> not logged in', () => { cy.origin('http://www.foobar.com:3500', { args: { username } }, ({ username }) => { cy.get('[data-cy="username"]').type(username) cy.get('[data-cy="cookieProps"]').type('SameSite=None') diff --git a/packages/driver/src/cy/commands/cookies.ts b/packages/driver/src/cy/commands/cookies.ts index 741c230d32..da9e92e708 100644 --- a/packages/driver/src/cy/commands/cookies.ts +++ b/packages/driver/src/cy/commands/cookies.ts @@ -21,7 +21,7 @@ function pickCookieProps (cookie) { // different defaults, and Firefox lacks support for `unspecified`, so // `undefined` is used in lieu of `unspecified` // @see https://bugzilla.mozilla.org/show_bug.cgi?id=1624668 -const VALID_SAMESITE_VALUES = ['no_restriction', 'lax', 'strict'] +const VALID_SAMESITE_VALUES = ['no_restriction', 'lax', 'strict', 'unspecified'] function normalizeSameSite (sameSite?: string) { if (_.isUndefined(sameSite)) { diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index fa8d539c21..60b5a6507c 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -27,5 +27,8 @@ }, "peerDependencies": { "eslint": "^9.31.0" + }, + "lint-staged": { + "**/*.{js,jsx,ts,tsx,json}": "eslint --fix" } } diff --git a/packages/server/lib/browsers/firefox.ts b/packages/server/lib/browsers/firefox.ts index f6f1793bf2..14efa53759 100644 --- a/packages/server/lib/browsers/firefox.ts +++ b/packages/server/lib/browsers/firefox.ts @@ -556,6 +556,8 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc debug('launch in firefox', { url, args: launchOptions.args }) + const launchEnvs = process.env + const geckoDriverOptions: GeckodriverParameters = { host: '127.0.0.1', // geckodriver port is assigned under the hood by @wdio/utils @@ -572,7 +574,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc stdio: ['ignore', 'pipe', 'pipe'], env: { ...BROWSER_ENVS, - ...process.env, + ...launchEnvs, }, }, jsdebugger: Debug.enabled(GECKODRIVER_DEBUG_NAMESPACE_VERBOSE) || false, diff --git a/packages/server/test/unit/browsers/firefox_spec.ts b/packages/server/test/unit/browsers/firefox_spec.ts index c082450eff..49f8ca5faa 100644 --- a/packages/server/test/unit/browsers/firefox_spec.ts +++ b/packages/server/test/unit/browsers/firefox_spec.ts @@ -65,7 +65,7 @@ describe('lib/browsers/firefox', () => { context('#open', () => { beforeEach(function () { // majorVersion >= 135 indicates BiDi support for Firefox - this.browser = { name: 'firefox', channel: 'stable', majorVersion: 135, path: '/path/to/binary' } + this.browser = { name: 'firefox', family: 'firefox', channel: 'stable', majorVersion: 135, path: '/path/to/binary' } this.automation = { use: sinon.stub().returns({}), } diff --git a/scripts/gulp/monorepoPaths.ts b/scripts/gulp/monorepoPaths.ts index 225d399431..bf2358f1c8 100644 --- a/scripts/gulp/monorepoPaths.ts +++ b/scripts/gulp/monorepoPaths.ts @@ -11,6 +11,7 @@ export const monorepoPaths = { pkgDriver: path.join(__dirname, '../../packages/driver'), pkgElectron: path.join(__dirname, '../../packages/electron'), pkgErrors: path.join(__dirname, '../../packages/errors'), + pkgEslintConfig: path.join(__dirname, '../../packages/eslint-config'), pkgExample: path.join(__dirname, '../../packages/example'), pkgExtension: path.join(__dirname, '../../packages/extension'), pkgFrontendShared: path.join(__dirname, '../../packages/frontend-shared'), diff --git a/system-tests/lib/system-tests.ts b/system-tests/lib/system-tests.ts index d74ca82fb7..82dd6c4af3 100644 --- a/system-tests/lib/system-tests.ts +++ b/system-tests/lib/system-tests.ts @@ -613,7 +613,12 @@ const systemTests = { const s = this.servers if (s) { - await Bluebird.map(s, stopServer) + try { + await Bluebird.map(s, stopServer) + } catch (err) { + console.error('Error stopping server', err) + throw err + } } }) }, diff --git a/system-tests/projects/e2e/cypress/e2e/cookies_spec_baseurl.cy.js b/system-tests/projects/e2e/cypress/e2e/cookies_spec_baseurl.cy.js index 86f254e2ef..c0813d74cf 100644 --- a/system-tests/projects/e2e/cypress/e2e/cookies_spec_baseurl.cy.js +++ b/system-tests/projects/e2e/cypress/e2e/cookies_spec_baseurl.cy.js @@ -11,9 +11,9 @@ const otherHttpsUrl = Cypress.env('otherHttpsUrl') let defaultSameSite = undefined if (Cypress.isBrowser('firefox')) { - // firefox will default to "no_restriction" + // firefox will default to "unspecified" // @see https://bugzilla.mozilla.org/show_bug.cgi?id=1624668 - defaultSameSite = 'no_restriction' + defaultSameSite = 'unspecified' } describe('cookies', () => { diff --git a/system-tests/projects/e2e/cypress/e2e/multi_cookies.cy.js b/system-tests/projects/e2e/cypress/e2e/multi_cookies.cy.js index 0a6f6efd0a..8baebeeae1 100644 --- a/system-tests/projects/e2e/cypress/e2e/multi_cookies.cy.js +++ b/system-tests/projects/e2e/cypress/e2e/multi_cookies.cy.js @@ -64,7 +64,13 @@ describe('set:cookies', () => { 'domain': '127.0.0.3', 'secure': false, 'httpOnly': false, - ...(Cypress.isBrowser({ family: 'firefox' }) ? { sameSite: 'no_restriction' } : {}), + ...( + Cypress.isBrowser('webkit') + ? { sameSite: 'no_restriction' } + : Cypress.isBrowser({ family: 'firefox' }) ? + { sameSite: 'unspecified' } : + {} + ), }, { diff --git a/system-tests/projects/e2e/cypress/e2e/request.cy.js b/system-tests/projects/e2e/cypress/e2e/request.cy.js index f6f8229555..3963e186fb 100644 --- a/system-tests/projects/e2e/cypress/e2e/request.cy.js +++ b/system-tests/projects/e2e/cypress/e2e/request.cy.js @@ -32,7 +32,7 @@ describe('redirects + requests', () => { } if (Cypress.isBrowser('firefox')) { - expectedCookie.sameSite = 'no_restriction' + expectedCookie.sameSite = 'unspecified' } expect(cookies[1]).to.deep.eq(expectedCookie)