Abstract:
Launch the Chrome extension background page and continue with the test.
How is it implemented?
All the pages are (Login, Admin pages) with the page object model.
Invoke the persistent context browser with the chrome extension URL and pass the page reference to each page constructor.
Use the page instance in the test and call the respective page actions.
What is a problem statement?
Able to launch the persistence context browser and navigate to the chrome extension URL.
However, page reference is passed to the target page and when it performs the action, it is throwing an error as the target is closed.
Sorry, I am new to Typescript. Came from java background.
Console Error:
Action was interrupted
public async search(): Promise<void> {
10 | console.log(this.browserPage.title());
| ^
11 | await this.browserPage.type('input[name="q"]', "Playwright")
Note: I have provided the google search for demonstration. But the approach is the same.
Test File: chromeextensionstest.spec.ts
import { BrowserContext, chromium, expect, Page, test } from "#playwright/test";
import path from "path";
import { SearchPage } from "../pages/admin/search.spec";
var browserContext: BrowserContext;
var browserPage: Page;
var basePath = require("path").resolve(__dirname, "..");
test.describe("test", async () => {
test.beforeAll(async ({ browser }) => {
browserContext = await chromium.launchPersistentContext("", {
headless: false,
channel: "chrome",
acceptDownloads: true,
recordVideo: {
dir: "videos",
size: { width: 640, height: 480 },
},
slowMo: 500,
strictSelectors: false,
//args: [
// `--disable-extensions-except=${extensionDir},${widgetDir}`,
// `--load-extension=${extensionDir},${widgetDir}`,
//],
});
browserContext.grantPermissions([
"notifications",
"clipboard-read",
"clipboard-write",
]);
browserPage = await browserContext.newPage();
});
test("Navigate to Google", async () => {
let extensionUrl = 'https://google.com';
await browserPage.goto(extensionUrl);
let searchPage = new SearchPage(browserPage);
searchPage.search();
});
});
Search POM: search.spec.ts
// #ts-check
import { expect, Page } from "#playwright/test";
export class SearchPage {
browserPage: Page;
constructor(page: Page) {
this.browserPage = page;
}
public async search(): Promise<void> {
console.log(this.browserPage.title());
await this.browserPage.type('input[name="q"]', "Playwright")
await this.browserPage.keyboard.press('Enter');
let text = await this.browserPage.innerText('//h3[contains(text(),"Playwright:")]')
expect(text).toContain('Playwright: Fast and reliable');
}
}
Related
I have a file structure that looks like this
Folder Structure
I have a file called "login.js" that will contain a function that logs into the page. Currently it looks like this
// login.js
const { chromium } = require('playwright');
async function Login() {
const browser = await chromium.launch({
headless: false,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('http://test.local/');
return true;
}
/*
This is just a example of logging in and not complet
*/
I want to export it so all my other tests can continue AFTER this one function logs in. Once it successfully logs in, tests such as 'example.spec.js' can get all the cookies/headers from the login script and continue
How can I do that?
You should be doing this.
// login.js
const { chromium } = require('playwright');
module.exports = async function login() {
const browser = await chromium.launch({
headless: false,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('http://test.local/');
return true;
}
Then you can access it in another file like this.
const login = require('./test.js'); // path has to be altered based on your folder structure
login();
Suppose, In a website I have some link to test that each link is working good.For that, I need to click each page link and need to test each page is opening and I need to assert the opened page content.
How's that possible using puppeteer?
If the links are common links with a href attribute, you can collect all URLs first and then test them in a loop like this:
const puppeteer = require('puppeteer');
(async function main() {
try {
const browser = await puppeteer.launch();
const [page] = await browser.pages();
await page.goto('https://example.org/');
const hrefs = await page.evaluate(() => {
return Array.from(
document.querySelectorAll('a[href]'),
a => a.href,
);
});
for (const url of hrefs) {
console.log(url);
await page.goto(url);
const data = await page.evaluate(() => {
return document.title;
});
console.log(data);
}
await browser.close();
} catch (err) {
console.error(err);
}
})();
I am new to Asp.net MVC Core. I am working on Server-side loading of JQuery Datatables.net using Asp.Net Core MVC Middleware.
I have used this tutorial to learn how to create a handler and then this article to migrate to middleware but are running into some issues that I hope you can help me with.
I have refined using this tutorial
I get error
"InvalidOperationException: Incorrect Content-Type: Microsoft.AspNetCore.Http.Features.FormFeature.ReadForm()"
when I run the solution.
Here is my code:
View
<script type="text/javascript">
$(document).ready(function () {
$('#datatable').DataTable({
//"paging": true,
//"ordering": true,
//"info": true,
'columns' : [
{ 'data': 'InsertedDateUtc' },
//{ 'data': 'EventId' },
{ 'data': 'UserId' },
{ 'data': 'Action' },
{ 'data': 'Context' },
{ 'data': 'RecordId' },
{ 'data': 'Property' },
{ 'data': 'OldValue' },
{ 'data': 'NewValue' },
],
'processing': true,
'serverSide': true,
'ajax' : {
'type' : 'POST',
'url' : '../AuditEventData.cs',
//'url': '../APIController/GetAuditEvents'
//'url' : '#Url.Action("GetAuditEvents", "APIController")'
'datatype': 'json',
}
});
});
</script>
Middleware
public class AuditEventData
{
private readonly RequestDelegate _next;
private readonly IDataGet _dataGet;
public AuditEventData(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
string result = null;
int filteredCount = 0;
var draw = httpContext.Request.Form["draw"].FirstOrDefault();
var start = int.Parse(httpContext.Request.Form["start"].FirstOrDefault());
var length = int.Parse(httpContext.Request.Form["length"].FirstOrDefault());
var sortCol = int.Parse(httpContext.Request.Form["columns[" + httpContext.Request.Form["order[0][column]"].FirstOrDefault() + "][name]"].FirstOrDefault());
var sortDir = httpContext.Request.Form["order[0][dir]"].FirstOrDefault();
var search = httpContext.Request.Form["search[value]"].FirstOrDefault();
try
{
var auditEvents = await _dataGet.GetServerSideAuditEvents(length, start, sortCol, sortDir, search);
filteredCount = auditEvents.Count();
var data = new
{
iTotalRecords = await _dataGet.GetTotalAuditEventCount(),
iTotalDisplayRecords = filteredCount,
aaData = auditEvents
};
result = JsonConvert.SerializeObject(data);
await httpContext.Response.WriteAsync(result);
}
catch (Exception e)
{
await ErrorHandler.HandleException(e);
}
await _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseAuditEventDataMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<AuditEventData>();
}
}
Startup.cs
app.MapWhen(
context => context.Request.Path.ToString().EndsWith("ViewAudit"),
appBranch =>
{
appBranch.UseAuditEventDataMiddleware();
});
In the middleware class the line
var start = int.Parse(httpContext.Request.Form["start"].FirstOrDefault());
gives me the error - the tutorials and Microsoft documentation here seem to indicate that I do not need to use the ".Form" and should be able to just use
var start = int.Parse(httpContext.Request["start"].FirstOrDefault());
however, when I do that, I get this error
cannot apply indexing with [] to an expression of type 'HttpRequest'
I cannot find any examples on how to do this and any help will be appreciated
Thanks
In order to expect to have a Form in your HttpContext.Request you must change your ajax datatype to 'application/x-www-form-urlencoded'. Now whether you want to do that is another question.
From here: https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data
I'm having trouble moving my Angular 1 JavaScript service to a Angular 2 TypeScript service using http to make a CORS request (this is using Ionic version 2). In Angular 1, I do something like this.
angular.module('app.services',[])
.factory('LoginService', ['$http', function($http) {
var service = {};
service.isUserLoggedIn = function() {
var restUrl = 'http://10.10.10.25:8080/api/user/isloggedin';
var options = {
method: 'GET', url: restUrl, withCredentials: true,
headers: {
'x-request-with': 'XMLHttpRequest',
'x-access-token': 'sometoken'
}
};
return $http(options);
};
return service;
}])
In Angular 2 using TypeScript, a lot has changed (Promises/Observables). My attempt so far looks like the following.
import { Injectable } from '#angular/core';
import { Headers, Http, Response, RequestMethod, RequestOptionsArgs } from '#angular/http';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class LoginService {
constructor(private _http:Http) {
}
isLoggedIn():boolean {
var r:any;
var e:any;
let url = 'http://10.10.10.25:8080/api/user/isloggedin';
let options:RequestOptionsArgs = {
url: url,
method: RequestMethod.Get,
search: null,
headers: new Headers({
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'x-access-token' : 'sometoken'
}),
body: null
};
this._http.get(url, options).map(this.extractData).catch(this.handleError)
.subscribe(
response => { r = <any>response; console.log(r); },
error => { e = <any>error; console.log(e); });
return false;
}
private extractData(response:Response) {
let body = response.json();
return body.data || { };
}
private handleError(error:any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.log('error = ' + errMsg);
return Observable.throw(errMsg);
}
}
I am no longer sure how to address in Angular 2 what I had in Angular 1
withCredentials (note that https://angular.io/docs/js/latest/api/http/index/RequestOptionsArgs-interface.html#!#withCredentials-anchor says the RequestOptionsArgs should have a field withCredentials but I get an error in the IDE Visual Studio Code saying that it does not exists in the interface; ionic is probably using an older angular 2 version?)
headers (x-request-with and x-access-token)
the actual response
I did a little bit more searching, and I was able to understand how to insert headers and properly handle the subscription. However, the withCredentials is still a problem.
I found this SO post angular2 xhrfields withcredentials true and modified my constructor as follows. It works now.
constructor(#Inject(Http) private _http:Http) {
let _build = (<any> _http)._backend._browserXHR.build;
(<any> _http)._backend._browserXHR.build = () => {
let _xhr = _build();
_xhr.withCredentails = true;
return _xhr;
}
}
The support of withCredentails will be present in the RC2 version of Angular2 that will be released soon. It's not part of the RC1 version... You need to wait a bit.
With RC2, you will be able to use this property directly in the request options:
this.get('...', { withCredentials: true })
See this question for more details:
Angular 2 - http get withCredentials
I used signalr self hosting with MVC and need to call it from client on another machine so I wrote code like that:
$(function () {
jQuery.support.cors = true;
$.connection.hub.url = "http://[server external Ip]:3031/signalr";
var chat = $.connection.CustomHub;
chat.client.addMessage = function (data, IMEI) {
//SomeCode
}
}
Everything going well but I have this error in Firefox Firebug:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http:// [server external IP]/signalr/negotiate?connectionData=%5B%7B%22name%22%3A%22customhub%22%7D%5D&clientProtocol=1.3&_=1400692033406. This can be fixed by moving the resource to the same domain or enabling CORS.
You have to enable Cross-Domain on the server application by installing Microsoft.Owin.Cors package and the calling UseCors() when starting up SignalR (assuming you are using SignalR 2.x). You do NOT need to specify jQuery.support.cors = true; in SignalR 2.0, actually you should remove it AFAIK.
It seems that the error refer to the network connection, we have already used signalR and the identification of the Url to hub is not mandatory.
Below an implementation of SignalR for Sales object:
1- Enable the service broker on the Database SQL Server:
ALTER DATABASE BlogDemos SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE ;
2- Install signalR from nuget
Install-Package Microsoft.AspNet.SignalR
3- Add reference to signalr javascript library if not added via nuget
<script src="/Scripts/jquery.signalR-2.2.1.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
4- Add Javascript to call the Hub
$(function () {
// Declare a proxy to reference the hub.
var salesNotificationsHub = $.connection.salesHub;
// Create a function that the hub can call to broadcast messages.
salesNotificationsHub.client.updateClientsales = function () {
Search();
//alert('des nouveaux sales');
};
// Start the connection.
$.connection.hub.start().done(function () {
alert('successful connection');
}).fail(function (e) {
alert(e);
});
});
5- Add the Search function created in step 4
function Search() {
grid.reload({ searchString: searchfields });
}
6- Creation of the code to load the Grid from GetList function in the
Controller
grid = $("#grid").grid({
dataKey: "Codsales",
uiLibrary: "bootstrap",
dataSource:"/sales/GetList",
autoLoad: true,
columns: [
{ field: "Codsales", title: "Code Demande", width: 200, sortable: true },
{ field: "Libelsales", title: "Libellé Demande", width: 200, sortable: true },
],
pager: { enable: true, limit: 10, sizes: [10, 20, 30, 40] }
});
7- Creation GetList function in the controller
public JsonResult GetList()
{
List<salesData> objsalesList = GetList().ToList();
return Json(objGridData, JsonRequestBehavior.AllowGet);
}
8- Create Function GetList Where will be attached the SqlDependency Object
public static List<salesData> GetList()
{
SqlDependency dependency = new SqlDependency();
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (var command = new SqlCommand(#"SELECT Codsales, Libelsales, Datsales,DatDetailledsales FROM [dbo].sales ", cn))
{
command.Notification = null;
dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (cn.State == ConnectionState.Closed)
cn.Open();
command.ExecuteNonQuery();
}
}
List<salesData> objList = new List<salesData>();
objList=Fetchsales(filterExpression, sortExpression, pageIndex, pageSize, out total);
rowsCount = total;
return objList;
}
private static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
salesHub.Updatesales();
}
}
9- Create Hub class
public class salesHub : Hub
{
[HubMethodName("updatesales")]
public static void Updatesales()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<salesHub>();
context.Clients.All.updateClientsales();
}
}
10- Configuration of the Sqldependency in Global.asax file
protected void Application_Start()
{ //Start SqlDependency with application initialization
string connString= ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
SqlDependency.Start(connString);
}
protected void Application_End()
{
//Stop SQL dependency
string connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
SqlDependency.Stop(connString);
}
Cordially