Search code examples
javascripttestingmockingsinonqunit

how to mock/test a window.prompt feature using sinon.js


I have a javascript function hat uses prompt to ask details from user:

export function askUserForTeamDetails() {
    const emoji = window.prompt( 'Enter new team’s emoji:' );
    if ( null === emoji ) {
        return;
    }
    const name = window.prompt( 'Enter new team’s name:' );
    if ( null === name ) {
        return;
    }
    return { name, emoji };
}

Then I have Qunit and Sinon.js setup inside an HTML file like this:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width">
            <title>Rusty Inc. Org Chart WordPress Plugin JavaScript Tests</title>
            <link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.7.1.css">
            
        </head>
        <body>
            <div id="qunit"></div>
            <div id="qunit-fixture"></div>
            <div  >
                <h1>Here is the playground</h1>
                <div id="playground"></div>
    
            </div>
           
            <script src="../node_modules/sinon/pkg/sinon.js"></script>
            <script type="module">
                import sinon from "../node_modules/sinon/pkg/sinon-esm.js";
                console.log('is sinon', sinon)
            </script>
              <script src="https://code.jquery.com/qunit/qunit-2.7.1.js"></script>
            <script type="module">
                import { subscribe, updateTree, updateSecretURL, getTree , createExpandCollapseCallback } from '../framework.js';
                import { ui , askUserForTeamDetails } from '../ui.js';
                const q = QUnit;
                
    
                q.test('test details function', assert => {
                let windowSpy = sinon.spy(window, 'prompt');

                //Dont  want  to call the  real function because it will cause propmts to display 

                let  askDetailsSub = sinon.stub(askUserForTeamDetails)
                const fake =  sinon.fake(askUserForTeamDetails);


                 // this gives an error
                sinon.assert.calledOnce(windowSpy); 

                //how to simulate the process of entering  inouts to prompt progamttaically?

                // verify that  the returned values are the ones inout by users


            } )
            </script>
        </body>
    </html>

I feel I have setup the fake function incorrectly and it's giving me an error: failing test description message

The text description of the error is:

Died on test #1     at https://923f663c4822.wp.prod1.devex.live/wp-content/plugins/rusty-inc-org-chart/tests/test.html:68:6: expected prompt to be called once but was called 0 times@ 6 ms
Source:     
AssertError: expected prompt to be called once but was called 0 times
    at Object.fail (https://923f663c4822.wp.prod1.devex.live/wp-content/plugins/rusty-inc-org-chart/node_modules/sinon/pkg/sinon.js:174:25)
    at failAssertion (https://923f663c4822.wp.prod1.devex.live/wp-content/plugins/rusty-inc-org-chart/node_modules/sinon/pkg/sinon.js:120:20)
    at Object.assert.<computed> [as calledOnce] (https://923f663c4822.wp.prod1.devex.live/wp-content/plugins/rusty-inc-org-chart/node_modules/sinon/pkg/sinon.js:149:17)
    at Object.<anonymous> (https://923f663c4822.wp.prod1.devex.live/wp-content/plugins/rusty-inc-org-chart/tests/test.html:76:18)

shouldn't the prompt be called?

I want to test the prompts are being called and then simulate entering values to the prompts. Also that the function returns the correct values. How Do I test it?


Solution

  • You don't have to mock the askUserForTeamDetails function but can rather stub the window.prompt function and fake taking an input by stubbing it's return value like this:

    let windowStub = sinon.stub(window, 'prompt');
    
    //fakes taking input by stubbing the return value of prompt
    // use `withArgs` to provide return value for the specific call
    windowStub.withArgs('Enter new team’s emoji:').returns(testEmoji);
    windowStub.withArgs('Enter new team’s name:').returns(testTeam);
    

    A solution would look like this:

    q.test('tes inputs function', assert => {
                    const testEmoji = `😊`;             
                    const testTeam = 'newTeama';
                    let windowStub = sinon.stub(window, 'prompt');
    
                    //fakes taking input but stubbing the return value of prompt
                    windowStub.withArgs('Enter new team’s emoji:').returns(testEmoji);
                    windowStub.withArgs('Enter new team’s name:').returns(testTeam);
                    //call the details functions
                    const answers = askUserForTeamDetails();
                    // restore/free window.prompt from stubbing
                    windowSpy.restore();
                    //perform the neessary assertions
                    assert.ok(  windowStub.withArgs('Enter new team’s emoji:').callCount === 1  , 'Prompt is opened for emoji' );
                    assert.ok(  windowStub.withArgs('Enter new team’s name:').callCount === 1  , 'Prompt is opened for  team’s name:' );
                    assert.ok( (answers.name === testTeam ) && (answers.emoji === testEmoji)  , 'Returns the values enter in prompt ' );
                    
    
                } )