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

Running multiple steps with Nightmare (need advice about refactoring) #708

Closed
alfonmga opened this issue Jul 1, 2016 · 12 comments
Closed

Comments

@alfonmga
Copy link

alfonmga commented Jul 1, 2016

Hello!
I would like to know how I could improve the code quality of this example script.
My main programming language isn't JavaScript so I am a bit lost about how I could make it better.

This example Nightmare script goes to accounts.google.com/signin and sign a Google account.
The script also checks if an element exist and if it not exists then the script will log an error so It can notify me when the bot is broken.

Here's the code:

var Nightmare = require('nightmare');
var uuid = require('uuid');

var accountEmailAddress = 'example@gmail.com';
var accountPassword = 'examplePassword';

var nightmare = Nightmare({
    show: true,
    webPreferences: {
        partition: uuid.v4()
    }
});

nightmare
    .goto('https://accounts.google.com/ServiceLoginAuth#identifier')
    .then(function(gotoResult) {
        if (200 === gotoResult.code) {
            return nightmare
                .exists('form[action*="https://accounts.google.com/signin"] [name=Email]')
                .then(function(elementExists) {
                    if (elementExists) {
                        return nightmare
                            .type('form[action*="https://accounts.google.com/signin"] [name=Email]', accountEmailAddress)
                            .exists('form[action*="https://accounts.google.com/signin"] [id=next]')
                            .then(function(elementExists) {
                              if (elementExists) {
                                return nightmare
                                    .click('form[action*="https://accounts.google.com/signin"] [id=next]')
                                    .wait('form[action*="https://accounts.google.com/signin"] [name=Passwd]')
                                    .exists('form[action*="https://accounts.google.com/signin"] [name=Passwd]')
                                    .then(function(elementExists) {
                                      if (elementExists) {
                                        return nightmare
                                            .type('form[action*="https://accounts.google.com/signin"] [name=Passwd]', accountPassword)
                                            .exists('form[action*="https://accounts.google.com/signin"] [id=signIn]')
                                            .then(function(elementExists) {
                                              if (elementExists) {
                                                return nightmare
                                                    .click('form[action*="https://accounts.google.com/signin"] [id=signIn]')
                                                    .wait(1500)
                                                    .exists('a[href*="https://myaccount.google.com"]')
                                                    .then(function(elementExists) {
                                                      if (elementExists) {
                                                        // account logged successfully
                                                        return nightmare.end();
                                                      } else {
                                                        // Element not found
                                                        // Send info about the error
                                                        // Repeat rabbitmq message
                                                        return nightmare.end();
                                                      }
                                                    });
                                              } else {
                                                // Element not found
                                                // Send info about the error
                                                // Repeat rabbitmq message
                                                return nightmare.end();
                                              }
                                            });
                                      } else {
                                        // Element not found
                                        // Send info about the error
                                        // Repeat rabbitmq message
                                        return nightmare.end();
                                      }
                                    });
                              } else {
                                // Element not found
                                // Send info about the error
                                // Repeat rabbitmq message
                                return nightmare.end();
                              }
                            });
                    } else {
                        // Element not found
                        // Send info about the error
                        // Repeat rabbitmq message
                        return nightmare.end();
                    }
                });
        } else {
            // cant access to google login page
            // Send info about the error
            // Repeat rabbitmq message
            return nightmare.end();
        }
    })
    .then(function() {
        console.log('ready to check next account');
        // ....
    });
@alfonmga
Copy link
Author

alfonmga commented Jul 5, 2016

Solved.

@alfonmga alfonmga closed this as completed Jul 5, 2016
@pinguinoholdings
Copy link

Can you share how you solved this? I'm also trying to loop over several exists() checks

@alfonmga
Copy link
Author

alfonmga commented Nov 2, 2016

@pinguinoholdings check out this repo, maybe it can help you.

@pinguinoholdings
Copy link

Thanks, I've been pounding through that all night. My code is working similar to yours, but it has a lot of nested .then() clauses that I'm hoping to clean up

@pinguinoholdings
Copy link

Thanks @alfonsomga

My use case was logging into a site, and then clicking through an unknown number of "OK" button prompts before I could get to the page I was looking for. Being an unknown number, I couldn't make it work by nesting .then() statements, not to mention how to exit the check. This is most likely not the most elegant solution, but it works.

For anyone following along later, hopefully this can provide a start...

nightmare
    .goto('http://example.com')
    .insert('[id=username', user) //fill in username
    .insert('[id=password', pass) //fill in password
    .click('[name=Submit]') //click submit
    .wait(3000) //nice and slow so we can watch what's being rendered
    .exists('[id=OK_Button]') //check if the OK_Button exists, it will return a boolean
    .then(function(result) {

        if(result)
        {
            console.log('We found the OK button on the first check');
            return okStillExists(nightmare) //now break out into a function to run the loop

        } else
        {
            console.log('OK Button does not exists');
            return 'Return anything you want here';

        }

    })
    .then(function(){
        nightmare
            .wait(3000)
            .click('Whatever selector you want click on')
            //do some work here if you want like screenshot/save an HTML file/etc
            .wait(3000)
            .click('#LogOff')
    })
    .then(function (result) {
        console.log('This is the first promise after we clicked log off: ' + result)
    })
    .then(function(result){
        console.log('About to end nightmare result: ' + result);
        return nightmare.end();
    })


//Run the following function as many times as necessary to check if the OK_Button still exists. We will keep running it until no longer prompted for confirmation
function okStillExists(nightmare)
{
    return nightmare
        .wait(3000) //wait 3 seconds to be fairly certain the page loaded
        .exists('[id=OK_Button]') //again this will return a boolean so we can check if the button exists
        .then(function(result)
        {
            if(result)
            {
                console.log('OK Button still exists')
                nightmare.click('[id=OK_Button]') //click it again so we can keep moving through the confirmations
                return okStillExists(nightmare) //and finally run the function again since it's still here
            } else
            {
                console.log('OK Button DOES NOT exists')
                return false;
            }

        })
}

@alfonmga
Copy link
Author

alfonmga commented Nov 2, 2016

@pinguinoholdings I would recommend you to use vo control flow library to achieve it.

Here's an example code:

var Nightmare = require('nightmare')
var vo = require('vo')

vo(run)(function(err, result) {
  if (err) throw err

  result.forEach(function (text) {
    console.log('#result > h3: ', text)
  })
})

function *run() {
  var nightmare = Nightmare();
  yield nightmare
    .goto('https://www.example.com/signin')
    .type('#login', 'username')
    .type('#password', 'password')
    .click('#btn')

  for (var i = 0; i < 4; i++) {
    yield nightmare
      .goto('https://www.example.com/page'+i)
      .wait(1000)
      .evaluate(function(){
        return $('#result > h3').text()
      })
  }

  yield nightmare.end()
}

As you can see it's pretty simple and straightforward.

@alfonmga
Copy link
Author

alfonmga commented Nov 2, 2016

By the way if you use vo control flow library you will can do things like:

var okButton = yield nightmare.exists('button[id="OK_Button"]');

if (okButton) {
  // ....
} 

I'm sure this will make your life easier!

@bitnom
Copy link

bitnom commented Jul 4, 2018

He saved the world

@nurettin
Copy link

By the way if you use vo control flow library you will can do things like:

var okButton = yield nightmare.exists('button[id="OK_Button"]');

if (okButton) {
  // ....
} 

I'm sure this will make your life easier!

I don't know if this worked back then, but now you can do the exact same thing just by replacing yield with await in an async function.

@alexey-sh
Copy link

it seems like puppeteer is better than nightmare

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'example.png'});
  await page.waitFor('.foo');
  await page.click('a.my-link');
  await page.keyboard.type('Hello World!');
  await page.keyboard.press('ArrowLeft');

  await page.keyboard.down('Shift');
  for (let i = 0; i < ' World'.length; i++)
    await page.keyboard.press('ArrowLeft');
  await page.keyboard.up('Shift');
  page.on('dialog', async dialog => {
    console.log(dialog.message());
    await dialog.dismiss();
    await browser.close();
  });
  page.evaluate(() => alert('1'));
  await page.keyboard.press('Backspace');
  await browser.close();
})();

@Josiassejod1
Copy link

Josiassejod1 commented Jul 9, 2022

hey for those looking for this answer, I was able to accomplish it doing it this way with no additional packages

(async () => {
    for (var i = 0; i < clientSites.length; i++) {
        const site = clientSites[i];
        console.log(site)
        await nightmare
          .goto(_prefix + clientSites[i])
          .wait()
          .screenshot(`screenshots/${site}.png`)
          .catch((error) => {
            console.error("Search failed:", error);
          });
      }

      await nightmare.end();
})();

@Josiassejod1
Copy link

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

No branches or pull requests

6 participants