Skip to content

Conversation

AtofStryker
Copy link
Contributor

@AtofStryker AtofStryker commented Jul 21, 2025

  • Closes N/A

Additional details

Converts the Cypress CLI from JavaScript to TypeScript. This is to help with our eventual goal of moving to an ESM based repository, which is much easier when compiling to source. The files in the package have been migrated to TypeScript and now compile down to CommonJS (for now).
Screenshot 2025-08-22 at 10 43 46 AM
Screenshot 2025-08-22 at 10 51 28 AM

We still only ship the JavaScript files with the CLI

Screenshot 2025-08-22 at 11 22 05 AM

It's important with this migration to make sure there are NOT breaking changes to the CLI. This is confirmed below:

Loaded from CommonJS (in comparison with the 15.0.0 CLI)

Screenshot 2025-08-22 at 11 18 27 AM

Loaded from ESM (in comparison with the 15.0.0 CLI)

Screenshot 2025-08-22 at 11 17 56 AM

With this, it is easier to visualize that there isn't a breaking change to the CLI.

What is in scope for this PR

  • update all JavaScript files, source and test, to TypeScript
  • Allow mocha to run TypeScript tests with ts-node
  • Fully remove all require() statements and use ES6 style import statements
  • compile the TypeScript down to CommonJS
  • preserve existing public API to avoid breaking change

What is not in scope for this PR

  • improving/changing the build process inside the CLI
  • perfecting the types used by the CLI. Many types will be any
  • rewriting then/catch to async/await
  • improving existing tests or migrating test harness

Steps to test

How has the user experience changed?

This change should go unnoticed to the end user as it is just a refactor

PR Tasks

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

Copy link

cypress bot commented Jul 21, 2025

cypress    Run #64974

Run Properties:  status check passed Passed #64974  •  git commit c1b0126335: Merge branch 'chore/refactor_cli_to_ts' of github.com:cypress-io/cypress into ch...
Project cypress
Branch Review chore/refactor_cli_to_ts
Run status status check passed Passed #64974
Run duration 11m 07s
Commit git commit c1b0126335: Merge branch 'chore/refactor_cli_to_ts' of github.com:cypress-io/cypress into ch...
Committer Bill Glesias
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 1
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 681
View all changes introduced in this branch ↗︎
UI Coverage  37.38%
  Untested elements 129  
  Tested elements 77  
Accessibility  97.35%
  Failed rules  3 critical   7 serious   1 moderate   1 minor
  Failed elements 62  

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@AtofStryker AtofStryker force-pushed the chore/refactor_cli_to_ts branch from 5e4e990 to b06e770 Compare July 21, 2025 22:20
cursor[bot]

This comment was marked as outdated.

@AtofStryker AtofStryker force-pushed the chore/refactor_cli_to_ts branch from b06e770 to 34448c3 Compare July 22, 2025 14:41
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

…s differently since the refactor and is printing non deterministic outputs into our tests that do not have a large impact on the area we are testing and mostly served to actually test the renders of the listr2 framework itself
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@AtofStryker
Copy link
Contributor Author

passing CI on 1ddfa5e empty commit

@@ -104,18 +113,18 @@ const runSmokeTest = (binaryDir, options) => {
const stdioOptions = _.extend({}, {
env: {
...process.env,
FORCE_COLOR: 0,
FORCE_COLOR: '0',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AtofStryker I've confused myself on whether this is actually testing what it should be testing. https://force-color.org/ This was the original PR: #28994

When this variable is present and not an empty string (regardless of its value), it should force the addition of ANSI color.

Wasn't the original intention to have this evaluate to OFF?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to turn it off because the colored ANSI output was causing out verification task to fail. This should still be testing what we want

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AtofStryker So FORCE_COLOR is evaluating to true here which is what we want?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would still evaluate to false (or at least should) because anything piped into execa would evaluate to a string as its an env variable in the process. It's the equivalent of doing something like FORCE_COLOR=0 node index.js where process.env.FORCE_COLOR inside index.js would actually be '0'

Screenshot 2025-08-22 at 4 33 28 PM


const rendererOptions = {
renderer,
}

const tasks = new Listr([
{
options: { title: util.titleize('Verifying Cypress can run', chalk.gray(binaryDir)) },
task: (ctx, task) => {
title: util.titleize('Verifying Cypress can run', chalk.gray(binaryDir)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AtofStryker I think this change is fixing behavior - this wasn't logging before (it's not in the snapshots) - is this the change in the snapshots that you were seeing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh duh that would explain it. It's been a while since I visited this PR and couldn't remember. Yes this was the change I saw in the snapshots not being deterministic. I'm going to double confirm by fixing this on develop and seeing if I see the same determinism problems

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this happens if you apply the fix on develop
Screenshot 2025-08-22 at 4 05 34 PM

Well I feel dumb now 🤣 . At least its working correctly. I will audit those tests and see if there are some additional assertions I can make that aren't so dependent on the snapshot

Comment on lines +78 to +79
const isPossibleLinuxWithIncorrectDisplay = (): boolean => {
return isLinux() && !!process.env.DISPLAY
Copy link
Member

@jennifer-shehane jennifer-shehane Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of feel like there are situations before where this may have not been working also?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possibly but unlikely? Any use cases you can think of?

Comment on lines +473 to 475
const { stdout } = await execa('uname', ['-m']).catch(() => ({ stdout: '' }))

debug('arm uname -m result: %o ', { stdout })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const { stdout } = await execa('uname', ['-m']).catch(() => ({ stdout: '' }))
debug('arm uname -m result: %o ', { stdout })
const stdout = await execa('uname', ['-m']).catch(() => ({ stdout: '' }))
debug('arm uname -m result: %o ', stdout)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what is implement currently is correct based on https://www.npmjs.com/package/execa/v/5.1.0#usage. the print statement is just electing to print this as an object

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just seems like double destructuring

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first is valid. The other wants to key an object with stdout in the print which I can see why but it seems unnecessary? Either way probably fine to leave as is

const getUrl = (arch, version) => {
if (is.url(version)) {
const getUrl = (arch: string, version: string): string => {
if (is.webUrl(version)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weirdly url is an alias of webUrl 🙄 so no change of behavior here but the type is right

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct I thought the same thing. One has types the other doesn't but it is the exact same thing!

sinon.stub(fs, 'removeAsync').resolves()
sinon.stub(state, 'getVersionDir').returns('/cache/Cypress/1.2.3')
sinon.stub(state, 'getBinaryDir').returns('/cache/Cypress/1.2.3/Cypress.app')
sinon.stub(state, 'getBinaryPkgAsync').resolves()
sinon.stub(fs, 'ensureDirAsync').resolves(undefined)
os.platform.returns('darwin')

;(os.platform as any).returns('darwin')
Copy link
Member

@jennifer-shehane jennifer-shehane Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AtofStryker Might prefer something like below - there's a lot of these leading semicolon cases which I don't think are super intuitive. Do we want to replace them all?

const platformStub = (os.platform as any)
platformStub.returns('darwin')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it reads better, but I don't know if it worth the effort to make this look better since we eventually plan on replacing mocha with vitest and will likely get rid of the spec_helper all together? I think we just live with the syntax for now and clean it up when we migrate the testing harness

@@ -345,7 +347,7 @@ describe('/lib/tasks/install', function () {
beforeEach(function () {
sinon.stub(util, 'isInstalledGlobally').returns(true)

state.getBinaryPkgAsync.resolves({ version: 'x.x.x' })
;(state.getBinaryPkgAsync as any).resolves({ version: 'x.x.x' })
Copy link
Member

@jennifer-shehane jennifer-shehane Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same - a few leading semicolons in this file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

personally I'm fine with it until we either improve the types or migrate the testing harness. The other sane option with spec files at least is to just us an @ts-expect-error as we get a lot of typing errors when we writing tests. something like:

// @ts-expect-error
state.getBinaryPkgAsync.resolves({ version: 'x.x.x' })

is completely normal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants