facebook / memlab

A framework for finding JavaScript memory leaks and analyzing heap snapshots
https://facebook.github.io/memlab/
MIT License
4.35k stars 118 forks source link

How to use memlab in websites which requires login credentials #55

Closed deepankarn closed 1 year ago

deepankarn commented 1 year ago

I am trying to use memlab in a website which accepts login credentials using ADFS. The first page should launch after entering the user name and password. How can this scenario be implemented using memlab?

Steps -

  1. Launch the application in Chrome
  2. Login to the application
  3. We land on the base page (take snapshot - s1)
  4. Search a patient
  5. Navigate to target page (take snapshot - s2)
  6. Navigate to base page (take snapshot - s3)
JacksonGL commented 1 year ago

@deepankarn Does the beforeInitialPageLoad callback work for your use case? It seems that you can do the login actions before the initial page load.

deepankarn commented 1 year ago

I am getting an error, "interaction failed" when I use "beforeInnitialPageLoad". In my website I have to do the initial page load. When I do the initial page load, the website redirects to ADFS for entering the username & password. Once the user enters the username & password, the website redirects it to the actual landing page. Hope that helps.

JacksonGL commented 1 year ago

@deepankarn Consider using the following scenario template.

let theUrlAfterLogin;

async function beforeInitialPageLoad(page) {
  await page.goto(loginUrl);
  // input login credentials and get the url after login is successful
  theUrlAfterLogin = ...
}

function url() {
  return theUrlAfterLogin;
}

// action where we want to detect memory leaks
async function action(page) {

}

// action where we want to go back to the step before
async function back(page) {

}

module.exports = {beforeInitialPageLoad, action, back, url};

If it doesn't work, please share your test scenario code (without the actual url and credentials) so that we can try to debug it

deepankarn commented 1 year ago

code in scenario.js file

let theUrlAfterLogin;

async function beforeInitialPageLoad(page) { await page.goto("https://XYZ"); // input login credentials and get the url after login is successful await page.waitForSelector('input[id=userNameInput]'); await page.$eval('#userNameInput', el => el.value = 'tw\schuser1');
await page.waitForSelector('input[id=passwordInput]'); await page.$eval('#passwordInput', el => el.value = 'xxxxx'); await page.click('[id="submitButton"]'); theUrlAfterLogin = "https://XYZ" }

function url() { return theUrlAfterLogin; }

// action where we want to detect memory leaks async function action(page) {

}

// action where we want to go back to the step before async function back(page) {

}

module.exports = {beforeInitialPageLoad, action, back, url};

OUTPUT FROM MEMLAB

C:\Users\dneogi>memlab run --scenario C:\Users\dneogi\AppData\Local\Temp\memlab\scenario.js 'command' is not recognized as an internal or external command, operable program or batch file. Cannot read property 'startsWith' of undefined total time: 905ms snapshot meta data invalid or missing Use memlab help or memlab <COMMAND> -h to get helper text

JacksonGL commented 1 year ago

@deepankarn I apologize for the issue in the template I provided earlier. Unfortunately, memlab does not currently support using a dynamically generated url after evaluating beforeInitialPageLoad. However, since your use case involves a url that is always the same, you can try using this revised code template:

async function beforeInitialPageLoad(page) {
  await page.goto("https://xyz/");
  // input login credentials and get the url after login is successful
  await page.waitForSelector('input[id=userNameInput]');
  await page.$eval('#userNameInput', el => el.value = 'tw\schuser1');
  await page.waitForSelector('input[id=passwordInput]');
  await page.$eval('#passwordInput', el => el.value = 'xxxxx');
  await page.click('[id="submitButton"]');
}

function url() {
  return "https://xyz/";
}

// action where we want to detect memory leaks
async function action(page) {

}

// action where we want to go back to the step before
async function back(page) {

}

module.exports = {beforeInitialPageLoad, action, back, url};
deepankarn commented 1 year ago

Thank you Jackson, I was able to run the modified code but was not able to login to my website. The snapshots that was captured by the MemLab tool did not show that the login credentials were sent to the website.

async function beforeInitialPageLoad(page) { await page.goto("https://XYZ/"); // input login credentials and get the url after login is successful await page.waitForSelector('input[id=userNameInput]'); await page.$eval('#userNameInput', el => el.value = 'tw\schuser1'); await page.waitForSelector('input[id=passwordInput]'); await page.$eval('#passwordInput', el => el.value = 'XXXXX'); await page.click('[id="submitButton"]'); }

function url() { return "https://XYZ/"; }

// action where we want to detect memory leaks async function action(page) {

}

// action where we want to go back to the step before async function back(page) {

}

module.exports = {beforeInitialPageLoad, action, back, url};

JacksonGL commented 1 year ago

@deepankarn It seems that the login process in the beforeInitialPageLoad callback did not complete successfully. If you are running memlab, you can try adding the --debug --headful flags to pause after the url callback and view the page in Chromium. This may help you troubleshoot the issue.

deepankarn commented 1 year ago

I was finally able to make the code work but got the below error -

============================ CODE =====================================

async function isPageLoaded(page) { await page.goto("https://twr1dweb4.rd.allscripts.com/TWClient/"); // input login credentials and get the url after login is successful await page.waitForSelector('input[id=userNameInput]'); await page.$eval('#userNameInput', el => el.value = 'tw\schuser1'); await page.waitForSelector('input[id=passwordInput]'); await page.$eval('#passwordInput', el => el.value = 'xxxxxx'); await page.click('[id="submitButton"]'); return true; }

function url() { return "https://twr1dweb4.rd.allscripts.com/TWClient/"; }

// action where we want to detect memory leaks async function action(page) {

}

// action where we want to go back to the step before async function back(page) {

}

module.exports = {isPageLoaded, action, back, url};

======================== ERROR =================================== C:\Users\dneogi>memlab run --scenario C:\Users\dneogi\AppData\Local\Temp\memlab\scenario.js --debug --headful 'command' is not recognized as an internal or external command, operable program or batch file. page-load(baseline)[s1] > action-on-page(target)[s2] > revert(final)[s3] Press Enter (or Return) to continue step-2: (node:11932) UnhandledPromiseRejectionWarning: Error: Cannot accept dialog which is already handled! at assert (C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\puppeteer\lib\cjs\puppeteer\common\assert.js:26:15) at Dialog.accept (C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\puppeteer\lib\cjs\puppeteer\common\Dialog.js:79:32) at BrowserInfo. (C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\@memlab\core\dist\lib\BrowserInfo.js:113:26) at Generator.next () at C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\@memlab\core\dist\lib\BrowserInfo.js:17:71 at new Promise () at __awaiter (C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\@memlab\core\dist\lib\BrowserInfo.js:13:12) at C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\@memlab\core\dist\lib\BrowserInfo.js:106:39 at C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\puppeteer\lib\cjs\vendor\mitt\src\index.js:51:62 at Array.map () (Use node --trace-warnings ... to show where the warning was created) (node:11932) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3) (node:11932) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. (node:11932) UnhandledPromiseRejectionWarning: Error: Cannot accept dialog which is already handled! at assert (C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\puppeteer\lib\cjs\puppeteer\common\assert.js:26:15) at Dialog.accept (C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\puppeteer\lib\cjs\puppeteer\common\Dialog.js:79:32) at C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\@memlab\api\dist\API.js:262:26 at Generator.next () at C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\@memlab\api\dist\API.js:17:71 at new Promise () at __awaiter (C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\@memlab\api\dist\API.js:13:12) at C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\@memlab\api\dist\API.js:261:39 at C:\Users\dneogi\AppData\Roaming\npm\node_modules\memlab\node_modules\puppeteer\lib\cjs\vendor\mitt\src\index.js:51:62 at Array.map () (node:11932) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 4) The page is reloaded. MemLab cannot analyze heap across page reloads. Please remove window.reload() calls, page.goto() calls, or any reload logic. Use memlab help or memlab <COMMAND> -h to get helper text

JacksonGL commented 1 year ago

@deepankarn It appears that your website has a pop-up dialog that is causing issues with Puppeteer when memlab tries to automatically accept it. I am unable to access the website you are testing, but one solution you could try is commenting out or adding a try-catch block around this line of code and using the local build of memlab.

Note: isPageLoaded is called every time after url, action, and back are invoked. I would recommend using beforeInitialPageLoad to log in to the website.