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

Clone command options before passing into commands #3171

Closed
lilaconlee opened this issue Jan 18, 2019 · 7 comments · Fixed by #6459 or m87h/site#83
Closed

Clone command options before passing into commands #3171

lilaconlee opened this issue Jan 18, 2019 · 7 comments · Fixed by #6459 or m87h/site#83
Labels
pkg/driver This is due to an issue in the packages/driver directory type: bug

Comments

@lilaconlee
Copy link
Contributor

Current behavior:

When commands receive options, it is the original object and not a clone. This has resulted in a few bugs (#2707, #365), and probably some more I couldn't quickly track down.

Desired behavior:

This should be done before options are passed into commands so all commands receive options in the same frozen state.

@flotwig
Copy link
Contributor

flotwig commented Nov 7, 2019

I don't think this is a good idea, because cloning every single argument sent to every single command unnecessarily is pretty wasteful and will add some slowdown to Cypress, especially if people are using large objects in commands.

I think that commands that use the identity of the object, or modify the object passed in, should clone options themselves as needed. We should probably do some kind of audit/add new tests to catch others like cy.setCookie that don't act right.

@bahmutov
Copy link
Contributor

bahmutov commented Nov 7, 2019 via email

@flotwig
Copy link
Contributor

flotwig commented Nov 7, 2019

I tried a little thing with Object.freeze and using use strict to make it err when you modify a property:

function callCommand(cmdFn, args) {
  'use strict'
  Object.freeze(args)
  try {
    cmdFn(args)
  } catch (e) {
    console.log('The options passed in were modified')
    console.log(e)
  }
}

callCommand(
  (args) => { args.b = 'foo' },
  { b: 'bar' }
)

...but since the callback is defined outside the use strict, this doesn't error unless you inline cmdFn, so just doing Object.freeze isn't enough to catch these issues.

I think if we just freeze random objects without visibility, it's likely to cause more bugs than it would solve.


Maybe we could use Proxy to create something like Object.freeze that actually errors when you modify a property on the passed-in object, then recursively apply this to command arguments when CYPRESS_ENV !== production or something.

@sainthkh
Copy link
Contributor

sainthkh commented Dec 2, 2019

@flotwig @bahmutov

The cause of this problem is the line like this in each command:

_.defaults options,
  $el: subject
  log: true
  force: false

(This line is from check.coffee)

As you know, _.default() mutates the object (in our case, options).

Because of this, freezing will just make problem bigger. (You cannot add default values to the options object.)

To solve this problem, we need to open every command file and fix all those lines like this (fix 1).

opts = _.default {}, options, 
  $el: subject
  log: true
  force: false

And change options with opts.

Or we can write like this (fix 2):

options = _.defaults {}, options, 
  $el: subject
  log: true
  force: false

In this case, you don't have to change options.

Personally, I prefer the first solution because it doesn't override given argument value.

As a matter of fact, I have a related PR (#5762). I had to open every command to show user-defined options value.

When I was writing it, I didn't know about this issue and thought options mutation was part of intended feature.

If you let me, I will revisit that PR and fix those _.default() with fix 1 or fix 2.

@flotwig
Copy link
Contributor

flotwig commented Dec 2, 2019

@sainthkh A PR to get rid of the _.defaults mutations would be welcome. You're right, this is unintended behavior, we don't want to modify what the user passes in at all.

@cypress-bot cypress-bot bot added stage: needs review The PR code is done & tested, needs review stage: work in progress and removed stage: needs review The PR code is done & tested, needs review stage: work in progress labels Dec 4, 2019
@cypress-bot cypress-bot bot added stage: needs review The PR code is done & tested, needs review and removed stage: work in progress labels Dec 16, 2019
@cypress-bot cypress-bot bot added stage: work in progress stage: needs review The PR code is done & tested, needs review and removed stage: needs review The PR code is done & tested, needs review stage: work in progress labels Jan 29, 2020
@cypress-bot cypress-bot bot added stage: work in progress and removed stage: needs review The PR code is done & tested, needs review labels Feb 12, 2020
@cypress-bot cypress-bot bot added the stage: needs review The PR code is done & tested, needs review label Feb 18, 2020
@cypress-bot cypress-bot bot added stage: pending release and removed stage: needs review The PR code is done & tested, needs review labels Apr 7, 2020
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Apr 7, 2020

The code for this is done in cypress-io/cypress#6459, but has yet to be released.
We'll update this issue and reference the changelog when it's released.

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Apr 13, 2020

Released in 4.4.0.

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

@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators Apr 13, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
pkg/driver This is due to an issue in the packages/driver directory type: bug
Projects
None yet
5 participants