Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cypress typescript .ts config file invalid after upgrade to Cypress 10 #21997

Closed
josh803316 opened this issue Jun 1, 2022 · 21 comments · Fixed by #22058
Closed

Cypress typescript .ts config file invalid after upgrade to Cypress 10 #21997

josh803316 opened this issue Jun 1, 2022 · 21 comments · Fixed by #22058
Assignees
Labels
CT Issue related to component testing v10.0.0 🐛 Issue present since 10.0.0

Comments

@josh803316
Copy link

Current behavior

I went through the upgrade process for Cypress 10. At the end of the upgrade after my top-level cypress.json file was converted to cypress.config.ts I got the following error.

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/xxx/git/core-web/cypress.config.ts
    at new NodeError (node:internal/errors:372:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:76:11)
    at defaultGetFormat (node:internal/modules/esm/get_format:118:38)
    at defaultLoad (node:internal/modules/esm/load:21:20)
    at ESMLoader.load (node:internal/modules/esm/loader:407:26)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:326:22)
    at new ModuleJob (node:internal/modules/esm/module_job:66:26)
    at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:345:17)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:304:34)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15)
    at async loadFile (/Users/xxxx/Library/Caches/Cypress/10.0.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:126:16)
    at async EventEmitter. (/Users/xxxx/Library/Caches/Cypress/10.0.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:136:32)

Here is my tsconfig.json file:

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "target": "es2015",
    "typeRoots": ["node_modules/@types"],
    "lib": ["es6", "dom", "es2017"],
    "module": "es2020",
    "resolveJsonModule": true,
    "baseUrl": "./",
    "skipLibCheck": true
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": false,
    "strictInputAccessModifiers": true,
    "strictTemplates": false
  }
}

Desired behavior

The config file and .ts extension should be valid.

Test code to reproduce

I included my typescript config file which should do the trick.

Cypress Version

10.0.0

Other

No response

@jennifer-shehane
Copy link
Member

@josh803316 Can you include your project's package.json? Do you have typescript installed?

@josh803316
Copy link
Author

Yes we are on a very recent version of typescript:

{
  "name": "core-web",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "heroku-prebuild": "echo \"//npm.pkg.github.com/:_authToken=${NPM_GITHUB_TOKEN}\" >> ./.npmrc",
    "ng": "ng",
    "serve": "ng serve",
    "serve:premerge": "pnpm run serve --configuration=premerge",
    "serve:staging": "pnpm run serve --configuration=staging-dev",
    "serve:preproduction": "pnpm run serve --configuration=preproduction",
    "serve:prod": "pnpm run serve --configuration=production",
    "build": "./scripts/build.sh",
    "post-build": "node ./scripts/post-build.js",
    "test": "ng test --source-map=false",
    "lint": "ng lint",
    "fix": "ng lint --fix",
    "e2e": "ng e2e",
    "tslint-check": "tslint-config-prettier-check ./eslintrc.json",
    "precommit": "npx --no-install lint-staged",
    "prepare": "is-ci || husky install"
  },
  "lint-staged": {
    "*.{js,json,scss}": [
      "prettier --write"
    ],
    "*.ts": [
      "prettier --write",
      "eslint --fix"
    ]
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "13.3.11",
    "@angular/cdk": "~13.3.8",
    "@angular/common": "13.3.11",
    "@angular/compiler": "13.3.11",
    "@angular/compiler-cli": "13.3.11",
    "@angular/core": "13.3.11",
    "@angular/fire": "^7.3.0",
    "@angular/forms": "13.3.11",
    "@angular/material": "~13.3.8",
    "@angular/platform-browser": "13.3.11",
    "@angular/platform-browser-dynamic": "13.3.11",
    "@angular/router": "13.3.11",
    "@fingerprintjs/fingerprintjs": "^3.3.3",
    "@fortawesome/fontawesome-free": "^6.1.1",
    "@ng-select/ng-select": "^8.1.1",
    "@stripe/stripe-js": "^1.31.0",
    "angular-progress-bar": "^1.0.11",
    "angular2-uuid": "^1.1.1",
    "compression": "^1.7.4",
    "core-js": "^3.22.7",
    "css-star-rating": "^1.3.0",
    "express": "^4.18.1",
    "firebase": "^9.8.2",
    "lottie-web": "^5.9.4",
    "ng2-simple-timer": "^11.2.9",
    "ngx-cookie-service": "^13.2.1",
    "ngx-pagination": "^6.0.1",
    "ngx-pipes": "^3.0.0",
    "ngx-slide-toggle": "^0.1.1",
    "ngx-star-rating": "~2.1.0",
    "ngx-stripe": "^13.2.1",
    "node-sass": "^7.0.1",
    "path": "^0.12.7",
    "rxjs": "^7.5.5",
    "tslib": "^2.4.0",
    "ua-parser-js": "^1.0.2",
    "uikit": "3.14.3",
    "zone.js": "~0.11.5"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^13.3.7",
    "@angular-eslint/builder": "13.2.1",
    "@angular-eslint/eslint-plugin": "13.2.1",
    "@angular-eslint/eslint-plugin-template": "13.2.1",
    "@angular-eslint/schematics": "13.2.1",
    "@angular-eslint/template-parser": "13.2.1",
    "@angular/cli": "13.3.7",
    "@angular/language-service": "13.3.11",
    "@ngx-env/builder": "^2.0.2",
    "@percy/cli": "^1.2.1",
    "@percy/cypress": "^3.1.1",
    "@sketchy/semantic-release-config": "^2.2.0",
    "@types/jasmine": "~4.0.3",
    "@types/jasminewd2": "~2.0.10",
    "@types/node": "^17.0.38",
    "@types/recurly__recurly-js": "^4.20.0",
    "@typescript-eslint/eslint-plugin": "5.27.0",
    "@typescript-eslint/parser": "5.27.0",
    "aws-amplify": "^4.3.24",
    "cypress": "^10.0.0",
    "cypress-terminal-report": "^3.5.2",
    "eslint": "^8.16.0",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-jsdoc": "latest",
    "eslint-plugin-prefer-arrow": "latest",
    "eslint-plugin-react": "latest",
    "human-signals": "^4.0.0",
    "husky": "^8.0.1",
    "is-ci": "^3.0.1",
    "istanbul-reports": "^3.1.4",
    "jasmine-core": "~4.1.1",
    "jasmine-spec-reporter": "~7.0.0",
    "karma": "^6.3.20",
    "karma-chrome-launcher": "~3.1.1",
    "karma-coverage": "^2.2.0",
    "karma-coverage-istanbul-reporter": "~3.0.3",
    "karma-firefox-launcher": "^2.1.2",
    "karma-jasmine": "~5.0.1",
    "karma-jasmine-html-reporter": "^2.0.0",
    "karma-safari-launcher": "^1.0.0",
    "karma-spec-reporter": "0.0.34",
    "lint-staged": "^13.0.0",
    "prettier": "^2.6.2",
    "protractor": "~7.0.0",
    "semantic-release": "^19.0.2",
    "ts-node": "^10.8.0",
    "tslint-config-prettier": "^1.18.0",
    "typescript": "4.6.4"
  },
  "engines": {
    "node": "v16.15.0",
    "npm": "8.8.0",
    "pnpm": ">=7.1.0"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "node ./.husky/commit-msg.js"
    }
  },
  "pnpm": {
    "peerDependencyRules": {
      "ignoreMissing": [
        "*"
      ]
    }
  }
}

@jennifer-shehane jennifer-shehane added stage: investigating Someone from Cypress is looking into this v10.0.0 🐛 Issue present since 10.0.0 labels Jun 1, 2022
@tgriesser
Copy link
Member

@josh803316 - we're pretty sure we've identified the issue here, and are working on a fix to get out shortly. To help confirm our assumptions, would you be able to share the cypress.config.ts that was generated?

@josh803316
Copy link
Author

@tgriesser Sure:

import { defineConfig } from 'cypress';

export default defineConfig({
  projectId: 'xxxxxx',
  defaultCommandTimeout: 10000,
  chromeWebSecurity: false,
  video: true,
  videoCompression: false,
  viewportWidth: 1440,
  viewportHeight: 768,
  e2e: {
    // We've imported your old cypress plugins here.
    // You may want to clean this up later by importing these.
    setupNodeEvents(on, config) {
      return require('./cypress/plugins/index.js')(on, config);
    },
    experimentalSessionAndOrigin: true,
    specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
  },
});

tgriesser added a commit that referenced this issue Jun 1, 2022
* fix: partial fix for #21997 & #22004, throw the originalError

* throw originalError test

Co-authored-by: Zach Bloomquist <git@chary.us>
@lmiller1990
Copy link
Contributor

Cypress 10.0.1 is out - can you give this another try @josh803316?

I tested using a project similar to yours, see here, and it worked on 10.0.1 - that said, I was not able to reproduce your original issue, even with the same tsconfig.json and package.json.

Please give 10.0.1 a try. If it is still giving you the same issue, are you able to share a slimmed down repository where the error occurs?

@josh803316
Copy link
Author

josh803316 commented Jun 2, 2022

@lmiller1990 That definitely got me further into the migration:

/Users/xxx/git/core-web/cypress/index.js:1
import { join } from 'path';
^^^^^^

SyntaxError: Cannot use import statement outside a module

I can probably re-work some of these JS files, but any idea how to support imports

This is my complete index.js file:

import { join } from 'path';

export default (on, config) => {
  on('before:browser:launch', (browser, launchOptions) => {
    console.log(config, browser, launchOptions);
    if (browser.name === 'chrome') {
      const ignoreXFrameHeadersExtension = join(
        __dirname,
        '../extensions/ignore-x-frame-headers'
      );
      launchOptions.args.push(
        launchOptions.args,
        push(`--load-extension=${ignoreXFrameHeadersExtension}`)
      );
    }
    return args;
  });
};

@josh803316
Copy link
Author

If I fix that import then I get the following which leads me to believe it's probably a setting in the tsconfig.json correct?

/Users/xxx/git/core-web/cypress/index.js:5
export default (on, config) => {
^^^^^^

SyntaxError: Unexpected token 'export'
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1033:15)
    at Module._compile (node:internal/modules/cjs/loader:1069:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object. (/Users/josh/git/core-web/cypress.config.ts:2:1)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Module.m._compile (/Users/josh/Library/Caches/Cypress/10.0.1/Cypress.app/Contents/Resources/app/node_modules/ts-node/src/index.ts:1455:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Object.require.extensions. [as .ts] (/Users/josh/Library/Caches/Cypress/10.0.1/Cypress.app/Contents/Resources/app/node_modules/ts-node/src/index.ts:1458:12)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)

@lmiller1990
Copy link
Contributor

lmiller1990 commented Jun 2, 2022

It's the same problem - it is expecting your .js files to be using CommonJS (require and module) as opposed to ES Modules (import and export). That's the Node.js default for .js files. You can see it in the stack trace:

at wrapSafe (node:internal/modules/cjs/loader:1033:15)

I was able to reproduce using your pluginsFile. I got it to work by adding allowJs: true to tsconfig.json, which let the TypeScript process the index.js instead of handing off to Node.js, which expects .js files to use CommonJS. By default, TS is only going to process .ts files, unless we tell it otherwise.

Another solution would be to rename cypress/plugins/index.js to cypress/plugins/index.ts. I think this is a little better.

The core issue is TypeScript was not processing the file (TS understands ES Modules out of the box - Node.js doesn't, at least without type: module or the mjs extension, which isn't really what you are looking for here).

We obviously need to handle this more gracefully - I will explore some options. I think the best option would be some specific logic to always use TS to process plugins/index.js, even if allowJs is not true, since many people migrating from Cypress 9.x will have that.

I hope this lets you move forward with migrating to Cypress 10 - if you still have an issue after trying one of those fixes, please post here so I can include them as part of the exploration around how to best handle the TS vs JS and conflicting module interop.

@josh803316
Copy link
Author

Thanks so much @lmiller1990 for the explanation. That did get me further along, but still shows me the following... which still seems to be in the same CommonJS area you were stating. I will try renaming/reworking the index.js to index.ts but this seems to be in a different place as it's emanating from the config file itself.

cypress.config.ts:4:28

TypeError: (0 , cypress_1.defineConfig) is not a function
    at Object. (/Users/josh/git/core-web/cypress.config.ts:4:28)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Module.m._compile (/Users/josh/Library/Caches/Cypress/10.0.1/Cypress.app/Contents/Resources/app/node_modules/ts-node/src/index.ts:1455:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Object.require.extensions. [as .ts] (/Users/josh/Library/Caches/Cypress/10.0.1/Cypress.app/Contents/Resources/app/node_modules/ts-node/src/index.ts:1458:12)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at loadFile (/Users/josh/Library/Caches/Cypress/10.0.1/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:103:14)
    at EventEmitter. (/Users/josh/Library/Caches/Cypress/10.0.1/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:146:38)
    at EventEmitter.emit (node:events:527:28)
    at EventEmitter.emit (node:domain:475:12)
    at process. (/Users/josh/Library/Caches/Cypress/10.0.1/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/util.js:33:22)
    at process.emit (node:events:527:28)
    at process.emit (node:domain:475:12)

@lmiller1990
Copy link
Contributor

lmiller1990 commented Jun 2, 2022

Hey @josh803316 this seems unrelated - the fact you are seeing that implies Cypress is not installed in your project locally. Are you installing Cypress globally? Are you using a monorepo with another version of Cypress 9.x it could be resolving instead?

I'm currently not able to reproduce what you are describing, but it looks like a different and unrelated error.

@josh803316
Copy link
Author

josh803316 commented Jun 2, 2022

@lmiller1990 I'm using pnpm/pnpx and I was running with pnpx cypress run or pnpx cypress open... but I also tried by doing:

  • ./node_modules/.bin/cypress cache clear and then
  • ./node_modules/.bin/cypress install
  • ./node_modules/.bin/cypress open

and I got the same error :(

I do see the same thing that you see:

❯ cat node_modules/cypress/index.mjs      
import module from 'module'

const require = module.createRequire(import.meta.url)

const cypress = require('./lib/cypress')

export default cypress

export const defineConfig = cypress.defineConfig

export const run = cypress.run

export const open = cypress.open

export const cli = cypress.cli

Hey @josh803316 this seems unrelated - the fact you are seeing that implies Cypress is not installed in your project locally. Are you installing Cypress globally? Are you using a monorepo with another version of Cypress 9.x it could be resolving instead?

I'm currently not able to reproduce what you are describing, but it looks like a different and unrelated error.

@lmiller1990
Copy link
Contributor

lmiller1990 commented Jun 2, 2022

You technically don't need defineConfig, it's actually just for type inference. If you just want it to work, you could just do export default { from your cypress.config.ts, and drop the defineConfig import.

That said, this should definitely work - I tried just now with pnpm and it was fine, but my setup may be different. My guess is it's resolving an older version of Cypress somehow (defineConfig is new to 10.x) but it's hard to debug. I wonder if pnpm is doing something in the background. Could you try:

const pathToModule = require.resolve('cypress');
console.log(pathToModule)

At the top of your cypress.config.ts and see what is printed - that would at least tell you where Cypress is getting resolved from, and might give us a hint. My guess is the module is somehow getting resolved from a different location.

@lmiller1990
Copy link
Contributor

Alternatively you could do a sanity check...

node -e "console.log(require('cypress'))"      

See if defineConfig is there. You could do the same in cypress.config.ts. I'm very curious as to what's going on here.

@josh803316
Copy link
Author

josh803316 commented Jun 2, 2022

@lmiller1990 I got it working by removing the defineConfig and using the export default {
Here is what the console.log prints

/Users/josh/git/core-web/cypress/index.ts

Here is what I got with your sanity check as well and defineConfig is there:

❯ node -e "console.log(require('cypress'))"
{
  open: [Function: open],
  run: [Function: run],
  cli: { parseRunArguments: [Function: parseRunArguments] },
  defineConfig: [Function: defineConfig]
}

Funny enough when I do it inside the config file I only get this:

{ default: [Function (anonymous)] }

@lmiller1990
Copy link
Contributor

lmiller1990 commented Jun 2, 2022

Something odd is happening when we resolve the module inside the Cypress process? If you are willing to help debug, what happens if you execute that default function and log the value in cypress.config.ts? Do you get the same result as the sanity check?

require.resolve('cypress') should not be printing /Users/josh/git/core-web/cypress/index.ts. It should print the location of the Cypress module it's using. That's very bizarre.

@josh803316
Copy link
Author

josh803316 commented Jun 2, 2022

@lmiller1990 Totally happy to help debug and appreciate your time. By the way the following may be of interest. I'm on node 16.5.0 and using nvm

NVM_DIR=/Users/josh/.nvm
NVM_CD_FLAGS=-q
NVM_BIN=/Users/josh/.nvm/versions/node/v16.15.0/bin
NVM_INC=/Users/josh/.nvm/versions/node/v16.15.0/include/node

What would be the best way to do that?

I tried:

let test = require('cypress');
console.log(`result=${test.default()}`);

and the result was: result=undefined so not as much help as I hoped.

@lmiller1990
Copy link
Contributor

lmiller1990 commented Jun 2, 2022

Weird. I can't think why that would be the case, or why you are even seeing what you are describing. 🤔

I'm also unsure how you had this working in 9.x - by default, using ES modules in cypress/plugins/index.js doesn't work in 9.x - I got the same error:

image

I wonder if I'm missing some configuration.

For debugging - I think going back and forth in comments is probably not ideal. Ideally, if you could provide a slimmed down repo I could clone, pre-migration, I could work through it to ensure the 9 -> 10 migration process is perfect. I'm not too sure how much trouble that would be, but if you could, it would be really useful.

@josh803316
Copy link
Author

@lmiller1990 I'll give it a shot, that will take some time so it will have to happen tomorrow as it's getting late here but I'll see what I can do. I'll revert back when I have something ready.

@lmiller1990
Copy link
Contributor

Thanks a lot @josh803316 🙏

@josh803316
Copy link
Author

@lmiller1990 I was able to create a public repo that demonstrates the issue:
https://github.com/sketchy/cypress10-upgrade-issue

@cypress-bot cypress-bot bot added stage: routed to ct and removed stage: investigating Someone from Cypress is looking into this stage: routed to ct labels Jun 2, 2022
@mjhenkes mjhenkes added stage: investigating Someone from Cypress is looking into this CT Issue related to component testing labels Jun 2, 2022
@mjhenkes mjhenkes added the PATCH label Jun 2, 2022
@mjhenkes mjhenkes linked a pull request Jun 2, 2022 that will close this issue
4 tasks
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Jun 2, 2022

Released in 10.0.2.

This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v10.0.2, please open a new issue.

@cypress-bot cypress-bot bot removed stage: pending release stage: investigating Someone from Cypress is looking into this labels Jun 2, 2022
@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators Jun 2, 2022
@mjhenkes mjhenkes removed the PATCH label Jun 2, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
CT Issue related to component testing v10.0.0 🐛 Issue present since 10.0.0
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants