Use PuppeteerSharp in AWS Lambda function - c#

I have a skill for the Amazon Echo. The lambda function is written in C#/.Net Core, and hosted on AWS. I now need to extend it with functionality that uses PuppeteerSharp. My first problem with this was that it downloads chromium and puts it in your application's directory by default. This gave me an error, as the filesystem is read only. I then tried the package HeadlessChromium.Puppeteer.Lambda.Dotnet, but my resulting skill was too large to upload as a lambda function, because it now had chromium embedded.
I'm now trying to override the default puppeteer downloadpath, using a BrowserFetchOption. This allows me to download chromium on AWS to the system temp path, but then Puppeteer can't find it. I need to add ExecutablePath to the LaunchOptions object when I try to launch puppeteer. I've tested this locally under windows and it works, I now just need to write some general code to find the chromium executable under AWS.
Is there a better way than searching for either chrome or chrome.exe under my download path (I need it to run under windows as well as AWS for the unit tests)? It feels like if Puppeteer provides this pair of options then there must be a better way to use them?
Is there a better way to use Puppeteer in a lambda function than what I'm attempting?
Here is the windows code that is currently working, with the chrome path hard coded:
string cpath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "chromium");
var bfopt = new BrowserFetcherOptions() { Path = cpath };
await new BrowserFetcher(bfopt).DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions {
ExecutablePath=System.IO.Path.Combine(cpath, "Win64-674921\\chrome-win\\chrome.exe"),
Headless = true
});

Related

PuppeteerExtraSharp Package No Errors But Browser Is Not Launching

I am trying to get puppeteer stealth working and found the "PuppeteerExtraSharp" package of NuGet. I however although no problems showing don't get the browser to launch and because I must use the async mehtod and don't get any errors whenever I run async I don't know how to fix the issue. I used the code that can be found on the official GitHub page of the publisher.
official link:
https://github.com/Overmiind/Puppeteer-sharp-extra
The code:
// Initialization plugin builder
var extra = new PuppeteerExtra();
// Use stealth plugin
extra.Use(new StealthPlugin());
// Launch the puppeteer browser with plugins
var browser = await extra.LaunchAsync(new LaunchOptions()
{
Headless = false
});
// Create a new page
var page = await browser.NewPageAsync();
await page.GoToAsync("http://google.com");
// Wait 2 second
await page.WaitForTimeoutAsync(2000);
// Take the screenshot
await page.ScreenshotAsync("extra.png");
I have also tried to define the path to my chrome and have tried the same for chromium and although it works when using the NuGet package "Puppeteersharp" is does not work with PuppeteerExtraSharp.
My end goal: is to run either playwright stealth or puppeteer stealth with C#

Puppeteersharp - redundant chromium command line arguments when using it in the container with .Net Core App

I have a task, that requires me of creating microservice, that uses puppeteersharp in order to make page screenshots. In order to do so, I use ASP.net core web api project template. In Startup.cs file I launch Puppeteersharp. Below is the code for that:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
Browser puppeteerBrowser = null;
Task.Run(async () => puppeteerBrowser = await LaunchPuppeteerBrowserAsync());
services.AddSingleton(puppeteerBrowser);
}
public static async Task<Browser> LaunchPuppeteerBrowserAsync()
{
Console.WriteLine("Starting to launch CHROMIUM...");
// Uncomment to let puppeteer download chromium
//await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
// Comment to let puppeteer run downloaded chromium
ExecutablePath = "/usr/bin/chromium",
Args = new[]{ "--no-sandbox", "--disable-gpu-rasterization", "--disable-remote-extensions" },
Headless = true,
// Didn't help
//IgnoredDefaultArgs = new []{ "--enable-gpu-rasterization", "--enable-remote-extensions", "--load-extension=" }
});
Console.WriteLine("CHROMIUM launched successfully");
return browser;
}
Further, the microsevice is required to run in the container inside our server. I created dockerfile, that uses standard docker-image for .Net Core applications from dockerhub, created by Microsoft. In order to Illustrate my problem, I created test application and uploaded it to github repo, you can find it here:
ASP.net Core Web Api project
There is a dockerfile, which can be used to build image like so:
*Please note, that I'm using Windows 10, and for test purposes I installed Docker onto my machine.
So, as you might know, docker images have layered structure. In order to take advantage of that feature, I decided to add the download and installation of chromium browser as 1 of the steps in dockerfile. That way, when request comes to server for the first time since launch, puppeteersharp library will not have to download the browser first and then do the job it required to do, and will use the binary (or whatever it is called in linux, I mean the application launching file; in windows that would be .exe).
You can see, that I provide executable path to Puppeteersharp browser when launching new browser in the code example I provided earlier (ExecutablePath = "/usr/bin/chromium").
The container can be started the following way:
And finally, I can describe my problem. First, using CMD command: docker exec -it e95e6d9fca63 bash I tune into container's bash.
There I run this:
In order to "ps" command to work. I also install "less" package (apt-get install less).
Then I run ps aux | less to show running processes inside the container. I get the following result, which show the command line parameters for chromium process, how it was launched. I underlined the ones, which bother me:
/usr/lib/chromium/chromium --show-component-extension-options --enable-gpu-rasterization --no-default-browser-check --disable-pings --media-router=0 --enable-remote-extensions --load-extension= --disable-background-networking --enable-features=NetworkService,NetworkServiceInProcess --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-breakpad --disable-client-side-phishing-detection --disable-component-extensions-with-background-pages --disable-default-apps --disable-dev-shm-usage --disable-extensions --disable-features=TranslateUI,BlinkGenPropertyTrees --disable-hang-monitor --disable-ipc-flooding-protection --disable-popup-blocking --disable-prompt-on-repost --disable-renderer-backgrounding --disable-sync --force-color-profile=srgb --metrics-recording-only --no-first-run --enable-automation --password-store=basic --use-mock-keychain --headless --hide-scrollbars --mute-audio about:blank --no-sandbox --disable-gpu-rasterization --disable-remote-extensions --remote-debugging-port=0 --user-data-dir=/tmp/kgkyt1d1.bqe
The latter --disable-gpu-rasterization --disable-remote-extensions arguments are the ones I set manually in Args property of LaunchOptions - check out the first code example I provided.
I also tried to use "IgnoredDefaultArgs" property of LaunchOptions, because according to documenation of the library, the options set there will be ignored. You can also see that happening in source code.
Launcher code - creates new chromium process, which has "PrepareChromiumArgs" method
The parameters put into "IgnoredDefaultArgs" array get deleted from the result array. But that didn't help, the --enable-gpu-rasterization --enable-remote-extensions --load-extension= are still there.
The strange thing is, that when I let the Puppeteersharp library to fetch the browser itself, and comment out "ExecutablePath" property from "LaunghOptions" and start it, there are no such redundant parameters. I have checked it.
My guess is that redundant arguments occur from the executable I downloaded when it gets started by the library. I mean in somewhat similar fashion that Windows allows you to add to file properties some extra command line arguments. But is this possible in Linux?
Can anyone please help?

Chrome driver is not able to pickup a file from the system when we run it through a Service

When I run Selenium Grid (Hub and Node) through a windows service and try to get a file from my system in my test cases through chrome driver, it shows "File not found on the path" at the sendkeys function of Selenium Webdriver, also if I do a File.Exist in the service the file is available and when I run it normally without service it is able to pick up the file with the same code.
Note: When we run test cases through service Web page and chrome is not visible and when we run normally web page and chrome is visible.
I am using Selenium Webdriver with C#.
public void FileUpload(IWebDriver driver,string pathOfFolder, string attachmentFile)
{
var filePath = Path.Combine(pathOfFolder, attachmentFile);
if(File.Exists(filePath))
{
driver.FindElement(By.XPath("//input[#type='file']")).SendKeys(filePath);
}
}
File is picked up from a shared drive(T:):
filepath = "T:\SampleFolder\FileUpload.xlsx";
The shared drive and the service have all the required permissions(I have used administrator permissions).
The problem arises when I do sendKeys with the path it gives file not found exception.

Launch the android Video App from my app

I am trying to create an app for Android 4.0.3 that listens for UDP-telegrams and starts other apps, depending on the received message.
I am already able to Launch some apps, like the "Music" app:
Intent intent = new Intent("android.intent.action.MUSIC_PLAYER");
intent.SetFlags(ActivityFlags.NewTask);
context.StartActivity(intent);
Is there any chance to do the same for the Video app?
I dont find a valid command for that. (especially WITHOUT loading a defined Video, like Action_View would do)
I was also thinking about using the StartApp, like the following:
OpenApp(context, "com.google.android.apps.maps");
But I also dont find a valid package-name for the Video app, like the other apps have.
Background:
This is for a car-project. The Android-tablet should be used as infotainment system and I want to use an "Ardoino Leonardo Ethernet" to switch between the most important app, using hardkeys instead of the touchscreen.
I prefer to avoid hardcoding any package names as doing so will cause the code to break on different APIs and different vendor's ROMs as they include their own "players" and "viewers".
You can determine which packages are installed that will response to different Uri and Mime types by using the Package Manager and making a query to it, i.e.
var activityIntent = new Intent(Intent.ActionView);
activityIntent.SetDataAndTypeAndNormalize(Uri.Parse("pseudo.mp4"), "video/mp4");
var resolvedActivityList = PackageManager.QueryIntentActivities(activityIntent, PackageInfoFlags.MatchAll);
foreach (var info in resolvedActivityList)
{
Log.Debug("SO", info.ActivityInfo.ApplicationInfo.PackageName);
}

http authorization with webdriver and popup window

I'm trying to use Selenium WebDriver to automatically login in to a site with a user-name and password. I've done my research and I don't believe this feature is supported by WebDriver, so I need to find another way. The site I'm trying to automate logging into is located here.
When prompted to login a popup window comes up that doesn't seem to be part of the browser. I'm using Firefox and Chrome. It seems Windows API may be required? I already tried passing the credentials in the URL but that didn't work. Also tried sendkeys, but received a Windows exception that the application was not accepting Windows messages. I also tried switching the current handle using driver.windowhandles but the popup doesn't seem to be a new handle.
Does anybody have any ideas? I'm kinda stuck. The preliminary code to get to the popup window is:
IWebDriver driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://www.portal.adp.com");
string currentWindow = driver.CurrentWindowHandle;
IWebElement userLogin = driver.FindElement(By.Id("employee"));
userLogin.Click();
The popup you are seeing is prompted by web server and is a authentication prompt. Selenium doesn't support this operation.
One of the way to handle this limitation is to pass user and password in the url like like below:
http://user:password#example.com
More info available here : http://aleetesting.blogspot.in/2011/10/selenium-webdriver-tips.html
I wanted my answer out there because I think I've solved it. This answer does not require passing the credentials through the URL (for those of you that are unable to like me). It also does not require any custom Firefox Profiles or extensions to be installed or included with the solution or installed onto the browser eliminating cross-machine compatibility issues.
The issue with me was that the authentication could not be completed via passing the credentials through the URL because the login was behind a proxy.
So, I turned to windows automation toolkits and found AutoIT. Using AutoIT and Selenium, you can login automatically by sending the username and password to the windows dialog that appears. Here's how (note the steps below are for c#:
1 - Download AutoIT from http://www.autoitscript.com/site/autoit/downloads/
2 - Add the autoit .dll to your project references.
Right click on references, select Add Reference. Next click the browse button and browse to the dll location (most default installations it will be c:\Program Files (x86)\AutoIt3\AutoItX\AutoItX3.dll), and add to project.
3 - use AutoIT and Selenium like this (assuming your web driver is already initialized):
//Initialize AutoIT
var AutoIT = new AutoItX3();
//Set Selenium page load timeout to 2 seconds so it doesn't wait forever
Driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(2));
//Ingore the error
try
{
Driver.Url = url;
}
catch
{
return;
}
//Wait for the authentication window to appear, then send username and password
AutoIT.WinWait("Authentication Required");
AutoIT.WinActivate("Authentication Required");
AutoIT.Send("username");
AutoIT.Send("{TAB}");
AutoIt.Send("password");
AutoIT.Send("{ENTER}");
//Return Selenium page timeout to infinity again
Driver.Manage().Timeouts().SetPageLoadTimeout(TimeSpan.FromSeconds(-1));
Anyway, that's it, and it works like a charm :)
Also note that there are some special characters that need to be escaped in AutoIT using the sequence "{x}". For example, if your password is "!tRocks", you'd need to pass it into AutoIT as "{!}tRocks".
Happy automating.
FirefoxProfile profile = new FirefoxProfile();
profile.SetPreference("network.http.phishy-userpass-length", 255);
profile.SetPreference("network.automatic-ntlm-auth.trusted-uris", hostname);
Driver = new FirefoxDriver(profile);
hostname is your URL (example.com) then try to
Driver.Navigate().GoToUrl(http://user:password#example.com);
I just got done working on a prototype project that is supposed to handle exactly this kind of situation.
It utilizes BrowserMob, a popular open source proxy, to perform the authentication.
SeleniumBasicAuthWrapper Hope it helps! It is still a work in progress, but hopefully we'll get any kinks or defects ironed out in the near future.

Categories