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

chore(server): convert remaining browsers code to typescript #23556

Merged
merged 13 commits into from
Aug 29, 2022

Conversation

flotwig
Copy link
Contributor

@flotwig flotwig commented Aug 25, 2022

  • Closes

User facing changelog

Additional details

  • This PR converts packages/server/lib/browsers/index.js to .ts and adds types for the BrowserLauncher implementations
  • Also converts electron.js to .ts, although I did not spend as much time adding types here
  • Converted Bluebird usage to native async/await as convenient
  • With these changes, everything in server/lib/browsers is finally TSified
  • Depends on refactor: move console logging out of run.ts #23555

Steps to test

How has the user experience changed?

PR Tasks

  • Have tests been added/updated?
  • [na] Has the original issue (or this PR, if no issue exists) been tagged with a release in ZenHub? (user-facing changes only)
  • [na] Has a PR for user-facing changes been opened in cypress-documentation?
  • [na] Have API changes been updated in the type definitions?

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Aug 25, 2022

Thanks for taking the time to open a PR!

@flotwig flotwig changed the title chore(server): convert browsers/index to typescript chore: convert browsers/index to typescript Aug 25, 2022
@cypress
Copy link

cypress bot commented Aug 25, 2022



Test summary

39641 0 3371 0Flakiness 0


Run details

Project cypress
Status Passed
Commit ab66d07
Started Aug 29, 2022 7:14 PM
Ended Aug 29, 2022 7:28 PM
Duration 14:31 💡
OS Linux Debian - 11.3
Browser Multiple

View run in Cypress Dashboard ➡️


This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard

@flotwig flotwig changed the title chore: convert browsers/index to typescript chore(server): convert remaining browsers code to typescript Aug 25, 2022
@flotwig flotwig marked this pull request as ready for review August 25, 2022 23:03
Copy link
Contributor

@lmiller1990 lmiller1990 left a comment

Choose a reason for hiding this comment

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

Cool, glad to see more TS, I hope we can get more and more strict. In general

  • seems some un-necesary async usage
  • consider assert over throw new Error (more concise and seems more idiomatic in my experience)

💯

@@ -294,6 +294,7 @@ function startAppServer (mode: 'component' | 'e2e' = 'e2e', options: { skipMocki
if (!ctx.lifecycleManager.browsers?.length) throw new Error('No browsers available in startAppServer')

await ctx.actions.browser.setActiveBrowser(ctx.lifecycleManager.browsers[0])
// @ts-expect-error this interface is strict about the options it expects
await ctx.actions.project.launchProject(o.mode, { url: o.url })
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like the interface is

export interface LaunchOpts {
  browser?: FoundBrowser
  url?: string
  automationMiddleware?: AutomationMiddleware
  projectRoot?: string
  shouldLaunchNewTab?: boolean
  onBrowserClose?: (...args: unknown[]) => void
  onBrowserOpen?: (...args: unknown[]) => void
  onError?: 
}

I wonder why we need the ts-expect-error? What is the error?

Copy link
Contributor Author

@flotwig flotwig Aug 26, 2022

Choose a reason for hiding this comment

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

It's only OpenProjectLaunchOpts at this point, which doesn't have url. This is an area that should be refactored, we're putting a lot of functionality into project.launchProject that makes it difficult to reason with (and difficult to sanely type)

debug('connecting to new chrome tab in existing instance with url and debugging port', { url: options.url })

const browserCriClient = this._getBrowserCriClient()

if (!browserCriClient) throw new Error('Missing browserCriClient in connectToNewSpec')
Copy link
Contributor

Choose a reason for hiding this comment

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

I think assert is more common for this kind of thing, eg

assert(this.ctx.currentProject, `Cannot checkIfFileExists without active project`)

    assert(browserCriClient, 'Missing browserCriClient in connectToNewSpec')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

assert doesn't seem to provide the type guarding needed, which is why I added this conditional. With assert, I still get browserCriClient is possibly 'undefined'

Copy link
Member

Choose a reason for hiding this comment

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

This should actually close #22451

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, at least it will give a clearer error message.

Copy link
Member

Choose a reason for hiding this comment

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

Well...it doesn't fix it but it just give a better warning. I remember talking with @ryanthemanuel about this

we close the browser on a retry:
https://github.com/cypress-io/cypress/blob/develop/packages/server/lib/modes/run.js#L1127
but likely don't flip the indicator that tells it to try and launch the browser from scratch


if (!options.url) throw new Error('Missing url in connectToNewSpec')

await this.attachListeners(browser, options.url, pageCriClient, automation, options)
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of marking this as asyncand doingawait this.attachListenershould we just removeasyncand thenreturn this.attachListeners? It looks like we do awaitwhere we call this function so marking it asasyncand then callingawait` at the end doesn't seem to do anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Readability-wise, I prefer awaiting a Promise over returning it, because it makes it obvious you're doing an asynchronous operation at the "end" of the function.

packages/server/lib/browsers/electron.ts Outdated Show resolved Hide resolved
packages/server/lib/browsers/electron.ts Outdated Show resolved Hide resolved
options = Windows.defaults(options)
// get our electron default options
// TODO: this is bad, don't mutate the options object
options = this._defaultOptions(projectRoot, state, options, automation)
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree mutation is the main thing that makes server so damn hard to work in :(

packages/server/lib/browsers/electron.ts Outdated Show resolved Hide resolved

if (browser.name === 'electron') {
return require('./electron')
return require('./electron') as typeof import('./electron')
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if this breaks things but should be able to do something like (I think)

return await import('./electron')

And get typing without as typeof. Also good to standardize on one syntax. Then getBrowserLauncher will return a Promise and well need to be awaited, though.

Or, we can just do

import chrome from './chrome'

Etc all at the top - not sure if there's any performance implication here, but moving towards proper ESM seems like a good idea, so maybe one day we can ship native ESM instead of CommonJS (although this is likely many years away, it would still be nice to work towards it).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since each browser requires different dependencies, it's done this way for performance. Whether or not it makes a big (or indeed, positive) impact... I'm not sure, but I'm not going to refactor the lazy-loading in this PR. I did switch to await import: ce15192

isProcessExit?: boolean
}

export type BrowserLauncher = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Yay, standardize browser launching interface, nice,

packages/server/lib/browsers/webkit.ts Outdated Show resolved Hide resolved
Windows.removeAllExtensions(win)

return Bluebird.map(extensionPaths, (extensionPath) => {
return Promise.all(extensionPaths.map((extensionPath) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍🏻

@@ -679,21 +696,7 @@ export = {

const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')

await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice deduping of this code here. Good clean up

Copy link
Collaborator

@ryanthemanuel ryanthemanuel left a comment

Choose a reason for hiding this comment

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

Nice work. Lots of good clean up here. The only thing I might question (though we do it a lot of places) with the new additional throw statements, do they fail gracefully or crash the server completely?

@flotwig
Copy link
Contributor Author

flotwig commented Aug 26, 2022

with the new additional throw statements, do they fail gracefully or crash the server completely?

@ryanthemanuel All the new throws are there for type guarding, where the type isn't providing a sufficient guarantee that a required property is defined in these places. Since our tests don't fail after these changes, I'm thinking it's unlikely users will see these errors. Server error handling should capture these as gracefully as any other browser launching error.

Base automatically changed from extract-logging-from-run to develop August 29, 2022 19:05
@flotwig flotwig merged commit 3c2fea2 into develop Aug 29, 2022
@flotwig flotwig deleted the browser-types branch August 29, 2022 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants