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

How can I execute a JavaScript command #897

Closed
paulpruteanu opened this issue Nov 9, 2017 · 18 comments
Closed

How can I execute a JavaScript command #897

paulpruteanu opened this issue Nov 9, 2017 · 18 comments

Comments

@paulpruteanu
Copy link

How can I execute javascript commands within a test?
I have a scenario when I need to run an app-level javascript command in order to proceed to the next step.

Thank you

@brian-mann
Copy link
Member

You have native access to every host object in your application.

cy.window().then((win) => {
  // call whatever you want on your app's window
  // so your app methods must be exposed somehow
})

@paulpruteanu
Copy link
Author

paulpruteanu commented Nov 9, 2017

Alright, so I do have a custom JavaScript method in my project that I cannot access it simply by doing

cy.window().then(win => myCustomMethod())

Would exposing it as window.myCustomMethod be the last resort?

@brian-mann
Copy link
Member

brian-mann commented Nov 9, 2017

Cypress can only access it the same way you could access it from your Dev Tools Console.

For instance - visit your app (outside of Cypress) and pop open the dev tools. That's the same boat as Cypress.

You have to expose the internals globally somewhere else its impossible to be accessed. So yes, setting on window is the only way.

I would recommend setting your App instance (or whatever everything is attached to) on window so you have access to all of them.

This is use case specific so without knowing more thats all I can say.

@paulpruteanu
Copy link
Author

Nice one. One observation though is that given

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        function myCustomMethod() {
            console.log('being hit')
        }
    </script>
</head>
<body>
foo bar
</body>
</html>

and

// cypress/integration/test.js
describe(`testing custom method run`, () => {
    it(`should run custom method`, () => {

        cy.visit('http://127.0.0.1:8080/')
        cy.window().then(win => win.myCustomMethod()) // this works
        cy.window().then(win => myCustomMethod()) // this fails
    })
})

you can't access the method globally, just as you'd do in Dev Tools Console. It happens to be inside window object, which does the trick.

@brian-mann
Copy link
Member

Right, I guess I meant that. The test specs are isolated from your application. They have to be because they exist before you visit anything! They have their own global context aka window.

When you do cy.window() you're then able to access the remote application's window as a local variable, which at that point, you use that context which is the same thing as the default context in Dev Tools.

@JimLynchCodes
Copy link

JimLynchCodes commented Mar 22, 2018

Thanks @brian-mann

    function someFunc() {
      console.log('oh, hey.');
    }

    cy.window().then( win => {
      win.someFunc();
    })

But I'm getting the error:

TypeError: win.someFunc is not a function

@dylanb
Copy link

dylanb commented Jun 19, 2018

How do I call an async function?

@dylanb
Copy link

dylanb commented Jun 19, 2018

I just found the answer - you return a promise-like thing

@brian-mann
Copy link
Member

@JimTheMan because you've created someFunc which is local to your specs, which has nothing to do with what's attached to the remote window. You'd just call someFunc() in your tests to invoke that function.

@dylanb you just return the invocation of an async function and if it returns a Promise in a cy.then or cy.invoke it will be awaited...

cy.window().invoke('someAsyncFunction', arg1, arg2, arg3) // will be awaited if returning promise

cy.window().then((win) => {
  return win.someAsyncFunction(arg1, arg2, arg3) // will also be awaited if returning promise
})

cy.window().then((win) => {
  return new Cypress.Promise((resolve, reject) => {
    win.someAsyncFunction(arg1, (err, val) {
      if (err) {
        return reject(err)
      }
      
      return resolve(val)
    })
  })
})

All the normal ways you interop with async. With bluebird there are even extra little helpers to make it easier to promisify node function signatures, etc.

@nitingupta220
Copy link

@brian-mann The issue that @JimTheMan is facing is the same that I'm facing.
And the solution that you've suggested to him I didn't completely understand.
Can you please elaborate?

@jennifer-shehane
Copy link
Member

@nitingupta220 Calling win.someFunc(); in his example would be attempting to invoke someFunc() from your application code, but someFunc was defined within the test files - not your app. Instead of win.someFunc();, you should just be able to call someFunc() inside your tests.

@flowt-au
Copy link

flowt-au commented Oct 26, 2018

Suggestion for Docs: Add info about using Cypress to test JS that has no UI.

I am looking at using Cypress to test modules / functions that have no UI. Much of the code is legacy. eg the following silly simple example:

    // app.js in http://127.0.0.1:8500/Sites/CypressTestApp/index.html

    var myFunc = {
        lastMsg : '',
        say: function(msg){
            console.log('Last msg: '+this.lastMsg)
            console.log('You said: '+msg)
            this.lastMsg = msg;
        },
        getLastMsg : function(){
            return this.lastMsg
        }
    }

It took me a while to find this issue post that answered the question: How do I test the function directly without reference to the DOM? Answer below.

    describe('The Home Page', function () {

        it('successfully loads', function () {
            cy.visit('http://127.0.0.1:8500/Sites/CypressTestApp')
            cy.window().should('have.property', 'myFunc')
        })
        
        it('Should set name to Jane',function(){

            cy.window().then(function(win){
                win.myFunc.say('Fred')
                win.myFunc.say('Jane')
                var lm = win.myFunc.getLastMsg()
                expect(lm).to.equal("Jane")
            })

        })
    })

Loving Cypress. This will save me months of work! Thanks.

@jennifer-shehane
Copy link
Member

Hey @flowt-au, If you want to open an issue on our docs, you can open it here.

@flowt-au
Copy link

flowt-au commented Nov 2, 2018

Done: 1109

Thanks.

@SaulGoodman1337
Copy link

SaulGoodman1337 commented Nov 16, 2018

Hi,
is it somehow possible to put the return value into a variable?

As an example:

On the console with the command gives:

require('settings!io.ox/mail').get('layout')

this back:

"list"

I want to put the whole thing in var viewmode to make a if query in the code later on

even more so:

  var viewmode = cy.window().then(win => win.require('settings!io.ox/mail').get('layout')))
  if (viewmode = xy) ...

@jennifer-shehane
Copy link
Member

@danielpondruff I think you should find our Variables and Aliases doc helpful.

@ganeshsirsi
Copy link

ganeshsirsi commented Jun 16, 2021

@procarrera
Copy link

Hello guys,
I am still trying to find a way to call a global function like this from Cypress. Do you have a way to do it?

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script>
        function myCustomMethod() {
            console.log('being hit')
        }
    </script>
</head>
<body>
foo bar
</body>
</html>

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

No branches or pull requests

10 participants