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: refactor server/lib/config.ts to pkg/config #22530

Merged
merged 45 commits into from
Aug 17, 2022

Conversation

sainthkh
Copy link
Contributor

@sainthkh sainthkh commented Jun 27, 2022

User facing changelog

N/A. Internal refactoring.

Additional details

  • Why was this change necessary? => Having them in pkg/server makes us create configApi inside DataContext and not be able to call them directly.
  • What is affected by this change? => None. It's a just code cleanup.

Any implementation details to explain?

At first, I thought this issue would be a simple copy and paste of a few lines of code. But I learned that it's a lot bigger than I thought.

To refactor server/lib/config.ts, I had to refactor these files, too:

  • server/lib/util/config.ts
  • server/lib/util/coerce.ts
  • server/lib/util/keys.ts
  • server/lib/util/origin.ts
  • server/lib/util/path_helpers.ts

Because they're used mostly inside config.ts and related with manipulating config values.


As we're migrating our code from commonjs to esm, the code structure also had to be fixed.

For this PR, I created 3 files to place the code.

  • config/src/project/index.ts -> The public api or the project config functions used outside the config package. I named them project because the type definitions were in ProjectConfigManager, ProjectLifecycleManager.
  • config/src/project/util.ts -> The internal functions to be used to implement the public api at index.ts. They are exported to be unit-tested.
  • config/src/util.ts -> Some utility functions like hideKeys and coerce are used outside pkg/config. They live here.

As for origin.ts, I moved it to pkg/network/lib/uri.ts because it more suits the place.


I renamed some functions to make it clear.

  • keys -> hideKeys
  • isDefault -> isResolvedConfigPropDefault

I added types for the functions. I added some any because I'm not sure about them now.

Steps to test

No new features; ensure existing tests are passing.

How has the user experience changed?

N/A. It's a code cleanup.

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 Jun 27, 2022

Thanks for taking the time to open a PR!

@sainthkh sainthkh force-pushed the issue-21781 branch 2 times, most recently from a75a5e9 to d5e2ebe Compare July 12, 2022 23:23
@cypress
Copy link

cypress bot commented Jul 12, 2022



Test summary

37848 0 469 0Flakiness 12


Run details

Project cypress
Status Passed
Commit e4e681c
Started Aug 5, 2022 2:35 AM
Ended Aug 5, 2022 2:57 AM
Duration 21:44 💡
OS Linux Debian - 11.3
Browser Multiple

View run in Cypress Dashboard ➡️


Flakiness

net_stubbing.cy.ts Flakiness
1 network stubbing > intercepting request > can modify the request body
xhr.cy.js Flakiness
1 ... > logs request + response headers
2 ... > logs Method, Status, URL, and XHR
3 ... > logs response
4 ... > logs request + response headers
This comment includes only the first 5 flaky tests. See all 12 flaky tests in the 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

import _ from 'lodash'
import path from 'path'

import { getProcessEnvVars, CYPRESS_SPECIAL_ENV_VARS } from '@packages/server/lib/util/config'
Copy link
Member

Choose a reason for hiding this comment

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

Any way we can drop pulling in @packages/server or @packages/data-context?

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'm trying. I guess we can remove @package/server from here.

Copy link
Member

Choose a reason for hiding this comment

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

So excited to see these changes!

@sainthkh sainthkh force-pushed the issue-21781 branch 2 times, most recently from a3c694f to 1d14c2b Compare July 18, 2022 01:41
import deepDiff from 'return-deep-diff'

import errors, { ConfigValidationFailureInfo, CypressError } from '@packages/errors'
import { getCtx } from '@packages/data-context/src/globalContext'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This dependency cannot be removed until we move getFilesByGlob to somewhere else.

Or call it inside data-context and pass the value to this function.

Maybe, this can be solved in the future PRs.

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 researched it after writing this comment. It seems that it will work if we add supportFiles member to setupFullConfigWithDefaults.

I'll do this after this PR is merged. This PR is big and I tried not to change code much.


const debug = Debug('cypress:config:project')

// TODO: any -> SetupFullConfigOptions in data-context/src/data/ProjectConfigManager.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using data-context/src/data/ProjectConfigManager.ts type definitions breaks the type. It seems that some types are missing. I postponed it for now because I need to read a lot of code to be sure that the new types are correct.

@@ -0,0 +1,49 @@
import type { DataContext } from './DataContext'
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 created this file to bypass circular dependency.

Copy link
Member

Choose a reason for hiding this comment

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

Where were you seeing this at?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

pkg/config uses getCtx and pkg/data-context uses pkg/config functions. It causes circular dependency and breaks Cypress.

This file could be removed and merged back to index.ts after fixing the problems I mentioned in this comment.

Copy link
Member

Choose a reason for hiding this comment

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

@sainthkh I think this can be removed now with your latest changes!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, it can't. As I wrote in the comment below, we need getCtx from pkg/data-context dependency in tests. Check this code

I thought about injection dummy functions to the test. But its return value is used to throw errors. So, it can cause false positive when getFilesByGlob is changed.

I think the only way to roll back this code is to:

  1. solve that obj.supportFile issue
  2. pass supportFile from pkg/data-context
  3. remove pkg/data-context from tests in pkg/config
  4. remove this globalContext file.

Copy link
Member

Choose a reason for hiding this comment

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

able to delete now with the latest commits?

Comment on lines +24 to 28
const parseClone = (urlObject: any) => {
return url.parse(_.clone(urlObject))
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is really weird code. url.parse expects string as its first arg, but our code is written for the case that it's not a string but an object.

I'll check this behavior and fix this in the part 2 of this PR.

Comment on lines -15 to +7
import type { ConfigValidationFailureInfo, CypressError } from '@packages/errors'
export const setupFullConfigWithDefaults = configUtils.setupFullConfigWithDefaults

import { getCtx } from './makeDataContext'
export const updateWithPluginValues = configUtils.updateWithPluginValues
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are here because they're used in some tests in pkg/server

expect(errors.throwErr).not.to.be.called
})
})

context('.get', () => {
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 tests under .get more suit pkg/data-context/test/unit/data/ProjectLifecycleManager.

If you agree, I'll move them in the next PR.

@@ -3,7 +3,7 @@
/* Basic Options */
"target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */
"lib": ["es2018", "ES2020.Promise"], /* Specify library files to be included in the compilation: */
"lib": ["es2018", "ES2020.Promise", "ES2021.String"], /* Specify library files to be included in the compilation: */
Copy link
Contributor Author

Choose a reason for hiding this comment

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

String.ReplaceAll is used in several pacakges. So, I added it here to avoid surprise.

@sainthkh sainthkh marked this pull request as ready for review July 18, 2022 03:16
@sainthkh sainthkh requested review from a team as code owners July 18, 2022 03:16
@sainthkh sainthkh requested review from rachelruderman and emilyrohrbough and removed request for a team July 18, 2022 03:16
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.

Glad to see the slow march from CJS+JS to TS, and gradual improvements to type safety. This generally looks like a pretty straight forward refactor - I don't see any obvious problems, just made a few recommendations around (potential) type safety improvements.

config.projectName = projectName

// @ts-ignore
return mergeDefaults(config, options, cliConfig)
Copy link
Contributor

Choose a reason for hiding this comment

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

How come we need to ts-ignore here?

Copy link
Contributor Author

@sainthkh sainthkh Jul 19, 2022

Choose a reason for hiding this comment

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

It's because of the many errors inside and outside mergeDefaults like these:

image

image

I'll check if I can add those types to the definitions and fix them in the subsequent PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Alright, I figured it was something like this.

packages/network/lib/uri.ts Outdated Show resolved Hide resolved
packages/network/lib/uri.ts Show resolved Hide resolved
packages/config/src/project/index.ts Show resolved Hide resolved
import deepDiff from 'return-deep-diff'

import errors, { ConfigValidationFailureInfo, CypressError } from '@packages/errors'
import { getCtx } from '@packages/data-context/src/globalContext'
Copy link
Member

Choose a reason for hiding this comment

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

We are calling this from the data-context package. Can we drop this dependency and just pass in the configFile as an additional parameter to updateWithPluginValues?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

getCtx is called in 2 functions:

  1. updateWithPluginValues of project/index.ts
  2. setSupportFileAndFolder of project/utils.ts

The solution for the first case is simple. We can simply add configFile member inside buildBaseFullConfig of ProjectConfigManager.

But the problem is the second one.

To call the ctx.file.getFilesByGlob, we need obj.supportFile. But the problem is that global supportFile option is legacy. It is splitted and moved under e2e or component from 10.

It means that current setSupportFileAndFolder can be buggy or completely removable in the current version. I'm not 100% sure about this. I need to think about a test case that shows this problem. Maybe #22776 is the related case.

The goal of this PR is copying and moving server/lib/config.ts to pkg/config and adding types as much as possible. I focused on this because this PR can be too big and it makes reviewers hard to review this PR.

I think checking/fixing bugs and adding more specific types should be done in the future PRs. What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

To call the ctx.file.getFilesByGlob, we need obj.supportFile. But the problem is that global supportFile option is legacy. It is splitted and moved under e2e or component from 10.

This log is correct, at this point we are testing the flattened configuration for the given testing type, so obj.supportFile is valid.

I think making a few small changes seems needed to move this code over and would be worth making a small change to remove this circular dependency. Simply passing int he attribute needed is probably the simplest approach until / if we ever want to revisit how this is composed.

// data-context/ProjectConfigManger.ts
setupFullConfigWithDefaults({
  projectRoot,
  projectName,
  configFile,
  config,
  envFile,
  options,
  cliConfig
}, 
ctx.file.getFilesByGlob)

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 never thought about passing the function as an argument to remove the dependency. Thanks for the idea.

Unfortunately, this idea even cannot completely remove pkg/data-context from config. It can be removed from src. But it had to be added to test to run tests.

However, I think this change is much better.

packages/server/lib/makeDataContext.ts Show resolved Hide resolved
packages/server/package.json Show resolved Hide resolved
packages/server/lib/util/args.js Outdated Show resolved Hide resolved
@@ -0,0 +1,49 @@
import type { DataContext } from './DataContext'
Copy link
Member

Choose a reason for hiding this comment

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

Where were you seeing this at?

@rachelruderman
Copy link
Contributor

Hi @sainthkh ! Can you update Steps to test in the description? I realize this is a refactor PR so it may be as simple as "No new features; ensure existing tests are passing"

@@ -0,0 +1,1182 @@
import '@packages/server/test/spec_helper'
import { getCtx } from '@packages/data-context/src/globalContext'
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 need globalContext here. Otherwise, we need to import the entire pkg/data-context.

Comment on lines +284 to +297
const supportFilesByGlob = await getFilesByGlob(obj.projectRoot, obj.supportFile)

if (supportFilesByGlob.length > 1) {
return errors.throwErr('MULTIPLE_SUPPORT_FILES_FOUND', obj.supportFile, supportFilesByGlob)
}

if (supportFilesByGlob.length === 0) {
if (obj.resolved.supportFile.from === 'default') {
return errors.throwErr('DEFAULT_SUPPORT_FILE_NOT_FOUND', relativeToProjectRoot(obj.projectRoot, obj.supportFile))
}

return errors.throwErr('SUPPORT_FILE_NOT_FOUND', relativeToProjectRoot(obj.projectRoot, obj.supportFile))
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Return value is used to throw errors.

Copy link
Contributor

@rachelruderman rachelruderman left a comment

Choose a reason for hiding this comment

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

@sainthkh
Copy link
Contributor Author

@rachelruderman For some reason, context was cleared before before in proxy_performance_spec. I solved the problem by adding new context before calling getCtx. I didn't create stub here because we're testing performance. Creating stub can make test good-for-nothing test result.

Copy link
Contributor

@rachelruderman rachelruderman left a comment

Choose a reason for hiding this comment

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

Looks great, thank you for your contribution @sainthkh !

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