Every time you do await browser.newPage(); - Puppeteer opens a new tab in the same browser window. But I need an actual new window with bookmarks bar and stuff, so popups open via JS won't work.
To open new windows (browser instances) you will need to call await puppeteer.launch() more times. Note that it will use more resources than if you'd open new tabs.
You might find useful the concept of BrowserContext in Puppeteer, where you can make use of browser.createIncognitoBrowserContext().
Example from the docs:
// Create a new incognito browser context
const context = await browser.createIncognitoBrowserContext();
// Create a new page inside context.
const page = await context.newPage();
// ... do stuff with page ...
await page.goto('https://example.com');
// Dispose context once it's no longer needed.
await context.close();
Related
Note: I am asking here a adapted version of this this closed question.
When using Puppeteer, it's easy to produce screenshots. It's even their first example, and it works fine on my web app:
const puppeteer = require('puppeteer');
const iPhone = puppeteer.devices['iPhone 6']; //iPhone 5/SE does not work
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(iPhone);
await page.goto('https://web.replayer.app', {
waitUntil: 'networkidle2',
});
await page.screenshot({ path: 'replayer-app.png' });
await browser.close();
})();
This produces a nice screenshot, but without device frame.
If I try to use one of the device names, that do produce a visual device frame in Google Chrome, like iPhone5/SE, I get
C:\Users\masu\Documents\node_modules\puppeteer\lib\cjs\puppeteer\common\Page.js:1500
this.setViewport(options.viewport),
^
TypeError: Cannot read properties of undefined (reading 'viewport')
at Page.emulate (C:\Users\masu\Documents\node_modules\puppeteer\lib\cjs\puppeteer\common\Page.js:1500:38)
at C:\Users\masu\Documents\puppeteer-question.js:7:14
at processTicksAndRejections (node:internal/process/task_queues:96:5)
However, since it's a web app, I would like to have the screen shot a device frame around it.
How to use and capture a screenshot from an emulated device with a viewport?
What is the cause of the issue?
I. You get this error because you are emulating the desired device with the wrong name. Even if DevTools UI names it "iPhone 5/SE" in reality DevTools Protocol only knows either "iPhone 5" or "iPhone SE" (you can give a try to give any false strings here, like "iPhone 2000", you will end up with the very same error msg). The available and valid device names are listed in DeviceDescriptors.ts on Github.
II. Since the referenced question was asked the situation is still the same: you cannot do a screenshot of the page with the device frames in puppeteer. Simply because those frames are part of the DevTools UI while Puppeteer is a high-level API to control Chrome over the DevTools Protocol: one is related to its frontend the other to its backend functionalities.
How you can proceed?
Implement the functionality in Node yourself. As I mentioned in the other post: it is easy to achieve it with node-images npm package if you have the device frames as transparent png file. E.g.: images('iPhone_5_portrait.png').draw(images('wikipedia.png').size(320), 130, 220).save('output.jpg').
Raise a feature request on Puppeteer's GitHub however it is not likely they plan to go this way (see point II. above).
Error: Element click intercepted only comes in headless chrome, it works fine if I run it on chrome browser without headless.
I have already tried below solutions:
Increasing window size in config chrome options to 'chromeOptions': { args: [ "headless","--disable-gpu", "--window-size=1920,1080" ] }
browser.actions().mouseMove(element).click(); and
browser.actions().mouseMove(element).click().perform();
Set window size in onprepare() method:
// set screen size setTimeout(function() { browser.driver.executeScript(function() { return { width: window.screen.availWidth, height: window.screen.availHeight }; }).then(function(result) { browser.driver.manage().window().setPosition(0,0); browser.driver.manage().window().setSize(result.width, result.height); }); });
Applied all types of waits: Sleep(), Implicit wait, Explicit wait, async wait: await.
Also used browser.driver.manage().window().maximize(); in before each instead of before all. But nothing of above worked. It worked for few tests but not for other.
I am running tests to check add user scenarios. Few of them are passed but not all. All tests are using same code written below only few things are changes as per test case like if it's adding a valid user, invalid user...
Basic common things in all add user script is below
Email:
Search a level on which I want to add a user:
Select Role:
click Add user button.
Problem I have observed is with search box and role dropdown. I think elements in headless chrome are not easily accessible or some other element is overlapping this elements.
Please help to solve this problem. Any help is appraised.
My tests -
it('1. should not add user with invalid email',async function(){
browser.waitForAngularEnabled(true)
browser.wait(EC.presenceOf(basepage.Loc_user_management),5000)
await basepage.Loc_user_management.click(); // click user management menu
browser.waitForAngularEnabled(true);
browser.wait(EC.presenceOf(userpage.Loc_add_user_btn),5000)
await userpage.Loc_add_user_btn.click();
browser.waitForAngularEnabled(true)
await userpage.Loc_email_addr_txtbox.click();
await userpage.Loc_email_addr_txtbox.sendKeys(loginData.users.invalid_username);
browser.manage().timeouts().implicitlyWait(1000);
browser.wait(EC.presenceOf(userpage.Loc_primarylevel_searchbox),5000);
userpage.Loc_primarylevel_searchbox.isDisplayed().then(async function(){
await userpage.Loc_primarylevel_searchbox.click();
await userpage.Loc_primarylevel_searchbox.sendKeys('Clients');
browser.actions().sendKeys(protractor.Key.ARROW_DOWN);
browser.actions().sendKeys(protractor.Key.ENTER);
//userpage.Loc_primarylevel_searchbox.click();
})
browser.sleep(500)
browser.waitForAngularEnabled(true);
browser.wait(EC.presenceOf(userpage.Loc_role_dropdown),5000)
await userpage.Loc_role_dropdown.click();
browser.waitForAngularEnabled(true)
await element(by.cssContainingText('option', 'Role1')).click();
browser.wait(EC.presenceOf(userpage.Loc_add_btn),5000)
userpage.Loc_add_btn.click().then(async function(){
expect(userpage.Loc_error_message_label.getText()).toEqual(errormessage.AddUserMessages.invalid_email_error_message);
})
});
that's LIKELY because you don't handle promises
browser.wait needs await
browser.waitForAngularEnabled also returns a promise and needs await
userpage.Loc_primarylevel_searchbox.isDisplayed().then doesn't wait until the element is displayed, it just gets a boolean if it's displayed or not and proceeds immediately. So in your case it's an extra line and complexity that doesn't do anything
sendKeys needs await
browser.sleep needs await
And don't mix .then and await. Use either one
What happens when you don't handle promises, is protractor just schedules the command, but doesn't wait until it's completed
Coincidentally, your script works with graphic interface, because chrome takes some time to load up. But when you run the same script in headless, it becomes faster (there is no UI to load) and executes one command before a previous is finished
I followed this guide from the Forge Community Blog.
The blog suggests loading an iFrame with the src attribute set to https://accounts.autodesk.com/Authentication/LogOut
<iframe src="https://accounts.autodesk.com/Authentication/LogOut" />
Though the iFrame loads properly, the user does not get logged out of the Forge platform.
This method worked well until sometime this past week or so. Now, the user remains logged in.
However, manually opening a new window and navigating to the LogOut URL does log the user out.
This appears to be a new change but I cannot find any documentation for it.
I used this in the past and so far I have not encountered any issue, basically goes to the Nodejs SDK to use the endpoint for sign out.
// prepare sign out
$('#signOut').click(function () {
$('#hiddenFrame').on('load', function (event) {
location.href = '/api/forge/oauth/signout';
});
$('#hiddenFrame').attr('src', 'https://accounts.autodesk.com/Authentication/LogOut');
// learn more about this signout iframe at
// https://forge.autodesk.com/blog/log-out-forge
})
To solve this, I decided to open a temporary new window for the logout url. I'm not happy with the solution, but it functions.
const newWindow = open('https://accounts.autodesk.com/Authentication/LogOut');
setTimeout(() => newWindow.close(), 500);
I want know if is it possible to make Google Chrome behave like a kiosk (without a frame or controls) but not in full screen, like the next mock picture:
My solution in Electron:
var app = require('app'); // Module to control application life.
var BrowserWindow = require('browser-window'); // Module to create native browser window.
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600, frame:false});
// and load the index.html of the app.
mainWindow.loadURL('http://www.google.com/');
// Emitted when the window is closed.
mainWindow.on('closed', function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
});
I do not think that it's possible to make Chrome itself behave that way, but a Chrome App (not an extension) can do this.
This is an option in window creation:
chrome.app.window.create("app.html", {
frame: "none"
});
Note that you will have to provide your own controls to close/move the window.
To make it behave like a browser, you'll need to embed a <webview> element. See also a browser app example.
However, note that Chrome Apps are being deprecated.
You should consider using a similar platform, like Electron or NW.js, to build your own "mini-browser" for your purpose.
I know you can inspect new websockets in Chrome via Network, but what about existing sockets? Let's say you open the devtools after the websocket was created. Some data has been sent and received already (don't care), and now I want to inspect the rest. Can I? Who browses with devtools always open? I want to inspect the current page's Websockets, without reloading of course.
I made this, but it requires at least 1 socket.send, and not all websockets do that:
(function() {
var s = WebSocket.prototype.send;
WebSocket.prototype.send = function(json) {
if (!this._oh) {
this._oh = 1;
this.addEventListener('message', function(e) {
console.log('receive', e.data);
});
}
console.log('send', json);
return s.apply(this, arguments);
};
})();
Using a bookmarklet or remote script is fine. I know an extension could do it (by overriding all WebSocket on every single page), but I rather not.