Trying to connect to external .net browser hosted in desktop application - playwright

I am trying to use playwright to interact with an external .net browser hosted inside a desktop application.
I'm able to connect to the application using connectOverCDP but wasn't able to interact with the elements.
import { test} from '#playwright/test';
const pw = require("playwright");
test('test', async ({ page }) => {
const browser = await pw.chromium.connectOverCDP("http://localhost:9222");
await page.locator('[aria-label="Screencast view of debug target"]').click(); // keeps failing here
});

Related

Keep browser open for all tests in a test suite

The typical structure of my tests is as follows:
import {test} from '#playwright/test';
test.describe('suite', () => {
test('test1', async ({page}) => {
await page.goto('test1');
});
test('test2', async ({page}) => {
await page.goto('test2');
});
});
This works perfectly but I noticed that Playwright opens and closes the browser window for each test and was wondering, why the browser window cannot stay open for all tests and if this could/should be optimised?
Based on my feedback from a playwright contributor:
If you use the VSCode extension, the browser stays open: https://playwright.dev/docs/getting-started-vscode
You want to ensure full test isolation on your CI, what Playwright does it guarantees that so no test will infer with each other. (less reasons to flake)

Electron app: How do you use ipcRenderer.sendToHost()?

In the Electron documentation for the webview tag, the following example is given to show how to communicate between the renderer process and the web page hosted in the webview:
With sendToHost method and ipc-message event you can easily communicate between guest page and embedder page:
// In embedder page.
const webview = document.getElementById('foo')
webview.addEventListener('ipc-message', (event) => {
console.log(event.channel)
// Prints "pong"
})
webview.send('ping')
// In guest page.
const {ipcRenderer} = require('electron')
ipcRenderer.on('ping', () => {
ipcRenderer.sendToHost('pong')
})
However, in my guest web page (inside the webview), I get Uncaught ReferenceError: require is not defined when I try to require('electron'), as indicated in the docs.
Is there something else I need to do to be able to require the ipcRenderer module from the guest web page?
Electron version: 1.4.6
Note: I'm not sure if this is important or not, but the page running inside my webview is served from a local server. In my top-level page in the renderer process, I do something like: document.getElementById("webview").src = "http://localhost:1234/...";.
Edit: It looks like serving my web page from a local server does not change anything. I have the same error after trying with a static HTML file. It looks like the example in the docs simply doesn't work, or I'm understanding it wrong.
// Simple foo.html somewhere on my computer
<script>
const {ipcRenderer} = require('electron')
ipcRenderer.on('ping', () => {
ipcRenderer.sendToHost('pong')
})
</script>
// In embedder page, in renderer process
document.getElementById("webview").src = "file://path/to/foo.html";
Output from the embedded page (inside the webview):
Uncaught ReferenceError: require is not defined
EDIT
For security reasons, the preferred way to use require in renderer processes is to use preload to inject only the minimum node integration your page requires. See point 2) of Electron's security recommendations. A minimal example for ipcRenderer:
// main.ts
const mainWindow = new BrowserWindow({
webPreferences: {
nodeIntegration: false,
preload: './preload.js'
}
})
mainWindow.loadURL('https://my-website.com')
// preload.js
const { ipcRenderer } = require('electron')
window.sendToElectron= function (channel) {
ipcRenderer.send(channel)
}
In your webpage you can now use window.sendToElectron("ping").
If you're using <webview> inside the renderer process, you can use <webview src="page.html" preload="./preload.js" /> to achieve the same result. So, that's what I would use to answer my original question, and inside preload.js I would inject a function that calls ipcRenderer.sendToHost("pong") in the global window.
Old answer (bad for security)
I had missed a vital point in the webview docs. To be able to call require from the page embedded inside the webview, you need to set the nodeintegration attribute on the webview tag:
<webview id="webview" nodeintegration />

File download from azure services hosted application get stuck for IE 11

We noticed that file download get stuck after some time in IE 11 client browser if downloaded from ASP.NET MVC application deployed to Azure service. The file download starts normally in IE 11. For several minutes the downloaded file size get increased. Then the yet downloaded file size get stuck with no more increases. If we continue to wait even more for around 1h, then the IE shows 'download was interrupted' error.
Azure instances have IIS 8.5 on board with app pool in default integrated pipeline mode. Application is ASP.NET MVC 5, targeted .NET 4.5.2.
There are no problems with download from Azure for Chrome browser.
There are no problems with download from local IIS express for both IE 11 and Chrome browsers.
There are no problems with download from local IIS 7.5 for both IE 11 and Chrome browsers.
So it looks like the only problem pair is Azure hosted IIS 8.5 + IE 11.
Application use the following code to stream file to client:
private const int StreamBufferSize = 1024*128; // 128KB
public static async Task StreamData(string fileName, Func<Stream, Task> streamFiller, HttpResponseBase response)
{
response.AddHeader("Content-Disposition", string.Format("attachment;filename=\"{0}\"", fileName));
response.ContentType = MimeMapping.GetMimeMapping(fileName);
response.BufferOutput = false;
using (Stream outputStream = new BufferedStream(response.OutputStream, StreamBufferSize))
{
await streamFiller(outputStream);
}
}
Where 'streamFiller()' is externally passed Func that writes data to the stream.
Please note that file is not so large, around 20Mb, but server do not send it at once. Instead server streams file with buffered chunks (see code above). Time between each chunk get streamed (buffer flashed) may be as long as several minutes.
By looking into Azure IIS I found that the client request looks like the following:
during normal download phase: State: ExecureRequestHandler, Module Name: ManagedPipelineHandler
since the download get stuck: State: SendResponse, Module Name: RemoveUnnecessaryHeadersModule.
To be clear what is RemoveUnnecessaryHeadersModule: we do the 'Server' header removing as following:
public class RemoveUnnecessaryHeadersModule : IHttpModule
{
public void Init(HttpApplication context)
{
// This only works if running in IIS7+ Integrated Pipeline mode
if (!HttpRuntime.UsingIntegratedPipeline) return;
context.PreSendRequestHeaders += (sender, e) =>
{
var app = sender as HttpApplication;
if (app != null && app.Context != null)
{
app.Context.Response.Headers.Remove("Server");
}
};
}
}

Ionic NTLM Authentication - IIS

I am building an iOS mobile application using the Ionic framework. The app will be accessing APIs that will be served by an ASP.NET 5 (MVC 6) application hosted on IIS using Integrated Windows Authentication. The server already has a web interface to it that uses an AngularJS client. I have been trying to get a $http call to the server from within an Ionic/Angularjs controller and have had no luck getting through the IIS Integrated windows authentication (I have tried running on the device/simulator as well as ionic serve). I always get a 401 Unauthorized error. I have tried setting withCredentials to true and passing in a username/password in the request with no luck. When I try to access the API URL from safari on an iPhone (a non-windows environment), I do get the Browser Authentication popup which successfully logs me in on entering my intranet windows username password.
I initially had some CORS issues that I have sorted through by adding the CORS service on the server side and also allowing all origins. I also have the proxy setup to avoid CORS issue when testing using ionic serve. Has anyone done something like this before? This is my controller code:
angular.module('starter.controllers', [])
.controller('AppCtrl', function($scope, $ionicModal, $http) {
$http.defaults.useXDomain = true;
$http.defaults.withCredentials = true;
// Form data for the login modal
$scope.loginData = {};
// Create the login modal that we will use later
$ionicModal.fromTemplateUrl('templates/login.html', {
scope: $scope
}).then(function(modal) {
$scope.modal = modal;
});
// Triggered in the login modal to close it
$scope.closeLogin = function() {
$scope.modal.hide();
};
// Open the login modal
$scope.login = function() {
$scope.modal.show();
};
// Perform the login action when the user submits the login form
$scope.doLogin = function() {
console.log('Doing login', $scope.loginData);
$http.post('http://localhost:8100/api/APIAccount/Login',{withCredentials:true})
.then(function(response)
{
console.log('success');
}, function(error) {
console.log('error');
});
};
});
After several hours of troubleshooting, it was as simple as setting up ASP.NET 5 CORS service to allow credentials. In my Startup.cs file in the ConfigureServices function I had to put in the following. Hope this helps someone else in the future.
services.AddCors(options =>
{
options.AddPolicy("AllowAllOrigins",
builder => builder.WithOrigins("http://<domainname>")
.AllowCredentials());
});

SignalR 2.0 - 404 in IIS Virtual Directory

I'm having an issue when deploying a very basic MVC5 app running SignalR 2.0.2. Everything works great in my local development environment when I'm running it with IIS Express. When I deploy to IIS, my js receives a 404 error attempting to connect to SignalR.
More specifically, I'm deploying to an application/virtual directory that is running under my Default Web Site. When I publish directly to Default Web Site, everything works successfully so IIS is not the issue.
GET http://myServer/signalr/negotiate?connectionData=%5B%5D&clientProtocol=1.3&_=1395517687175 404 (Not Found)
I'm assuming the 404 is caused by the missing application name. ie: myServer/MyApp/signalr/negotiate...
I've searched a number of posts and SignalR documentation with no luck regarding IIS and Applications/Virtual Directories and SignalR. Below is snippets of code in my app.
Thanks!
JS:
var connection = $.hubConnection();
var proxy = connection.createHubProxy('TestHub');
connection.start()
.done(function () {
console.log('Now connected, connection ID=' + connection.id + ' using transport=' + connection.transport.name);
})
.fail(function () { console.log('Could not connect'); });
Startup.cs:
app.MapSignalR();
Update
By changing the following JS code I was able to 'fix' the issue. The question is, how proper is this?
//var connection = $.hubConnection();
var connection = $.hubConnection("/MyApp/signalr", { useDefaultPath: false });
Your fix seems reasonable.
{ useDefaultPath: false } simply tells SignalR not to append "/signalr" to the url, so you could also create your connection object like this: var connection = $.hubConnection("/MyApp");
Alternatively, if you want to use JS hub proxies generated at /MyApp/signalr/hubs, you can could connect like this:
var proxy = $.connection.testHub;
// Make sure you always wire up client methods before calling start
proxy.client.myClientMethod = function () { /* ... */ };
$.connection.hub.start()
.done(function () { /* ... */ })
.fail(function () { /* ... */ });
http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-javascript-client#genproxy
A solution which will work in dev, and in IIS hosted as application, virtual directory or root is to configure the hub using the page url as its base. This will mean you won't need to hard code the value and negates configuration change for development and deployed scenarios.
var connection = $.hubConnection(document.location.origin + document.location.pathname);

Resources