How to get text out of ElementHandle? - c#

Using PuppeteerSharp, I am trying to get the text of the element.
ElementHandle elementHandle = await page.XPathAsync("//html/body/div[1]/section/div/section/h2")[0];
Now that I have the element handle, how do I actually get the text from it? I don't see any obvious methods. I would have expected TextAsync or something similar, but I don't see it.
Using PuppeteerSharp 5.0.

You can call EvaluateFunction passing that ElementHandle as an argument
var content = await Page.EvaluateFunctionAsync<string>("e => e.textContent", elementHandle);
If you have many scenarios like that, you can build an extension method to solve that for you ;)

#Botan, thank you!
I have tried (in VB.NET) and found:
(Await elementhandle.GetPropertyAsync("innerText")).ToString
result: "JSHandle:foo", but
(Await elementhandle.GetPropertyAsync("innerText")).RemoteObject.Value.ToString
result: "foo"

If your are after a strongly typed API for use with Puppeteer Sharp then you can use PuppeteerSharp.Dom which is available on Nuget.org.
// Add using PuppeteerSharp.Dom to access the extension methods
ElementHandle elementHandle = await page.XPathAsync("//html/body/div[1]/section/div/section/h2")[0];
// Create a strongly typed HtmlHeadingElement object
var headingElement = elementHandle.ToDomHandle<HtmlHeadingElement>();
// You'll now have context specific methods relevant to HtmlHeadingElement
//Get TextContent via the async method
var textContext = await headingElement.GetTextContentAsync();
var innerText = await headingElement.GetInnerTextAsync();
There's a number of QuerySelector extension methods also, so you can avoid the ToDomHandle method if you are using a query selector.
var element = await page.QuerySelectorAsync<HtmlElement>("#myElementId");
There are more examples on the GitHub page.

Related

Pupperteer-sharp Get innerText of Clicked element

How do I get the inner text of an element that has been click?
string xplast = "//button[#id='test']";
IElementHandle last = await ((IPage)page).WaitForXPathAsync(xplast);
await last.FocusAsync();
await last.ClickAsync();
string innertext = (await last.GetInnerTextFromElement()).ToString()
is there a way to do something like this? string innertext = (await last.GetInnerTextFromElement()).ToString()
I have now tested that var text = (await last.GetPropertyAsync("innerText")).RemoteObject.Value.ToString(); works fine, at least here on Windows.
If you'd prefer a strongly typed experience then PuppeteerSharp.Dom provides a set of extensions to PuppeteerSharp.
Install PuppeteerSharp.Dom from Nuget.org then you can use the strongly typed extensions.
// Add using PuppeteerSharp.Dom; to access WaitForXPathAsync<T>
string xplast = "//button[#id='test']";
var last = await page.WaitForXPathAsync<HtmlButtonElement>(xplast);
await last.FocusAsync();
await last.ClickAsync();
var innerText = await last.GetInnerTextAsync();

How to check if a tweet was sent with tweetinvi [duplicate]

I am relatively new to programming in C# (Learning on my own for a school project) and decided to try using TweetInvi to implement Twitter functionality.
So far it's going good, got the authentication and publishing up and running, but I'm struggling to find out how to use the DestroyTweet() method.
It, and many other methods takes a tweetID parameter, which I can't figure out of how to find for a specific tweet.
Using the following code to publish a tweet, how can i find the tweetID of this tweet?
public ITweet publishTweet(string text)
{
return Tweet.PublishTweet(text);
}
// Snippet from a test method in main class.
twitter.twitterUser.publishTweet(System.Console.ReadLine());
// Still working on GUI so using ReadLine for now.
It's probably an easy solution, but I just can't figure it out!
Thanks in advance.
You can try something like this:
public string PublishTweet(string text)
{
var appCredentials = new TwitterCredentials(_apiKey,_apiSecret, _accessToken, _accessTokenSecret);
Tweetinvi.Auth.SetCredentials(appCredentials);
text = "my tweet";
var publishedTweet = Tweetinvi.Tweet.PublishTweet(text);
var tweetId = publishedTweet.Id.ToString();
return tweetId;
}
You just need to get the published tweet into a var for the result of the PublishTweet() method then you select the field(s) you need.
Simple solution. As explained before you need to take the tweet back from PublishTweet.
string text = "text";
ITweet tweet = Tweet.PublishTweet(text);
bool destroySuccess = tweet.Destroy();

Cannot update read/write attributes of UserPoolClient via C# SDK

When programmatically creating a Cognito user pool and app client, if the app client is to have read/write access to attributes of the user pool, that access must be explicitly given. I have been able to do so successfully for custom attributes but built-in attributes always return an error of "Invalid write attributes specified while creating a client" or "Invalid read attributes specified while creating a client".
Documentation is ... both voluminous and difficult to find. I have yet to see an example of this or an actual bit of useful documentation on the CreateUserPoolClientRequest type that says anything about this other than things like "ReadAttributes is a list of strings that are the attributes that can be read".
Here is the code I'm using that always ends up with that error message and failure to create the app client. _client is an AmazonCognitoIdentityProviderClient properly instantiated and credentialed and running in a lambda function.
var request = new CreateUserPoolClientRequest { UserPoolId = userPoolId, ClientName = $"{name}AppClient" };
var builtInAttributes = new List<string>()
{
"address","birthdate","email","family name","gender","given name","locale","middle name","name","nickname","phone number", "picture","preferred username","profile","zoneinfo","updated at","website"
};
var readAttributes = new List<string>();
var writeAttributes = new List<string>();
readAttributes.InsertRange(0,builtInAttributes);
writeAttributes.InsertRange(0, builtInAttributes);
var attributeConfig = ConfigurationHelper.GetListFromSection("UserPoolCustomAttributes");
foreach (var attribute in attributeConfig)
{
readAttributes.Add($"custom:{attribute.Key}");
writeAttributes.Add($"custom:{attribute.Key}");
}
request.ReadAttributes = readAttributes;
request.WriteAttributes = writeAttributes;
var result = await _client.CreateUserPoolClientAsync(request, CancellationToken.None);
Any help is greatly appreciated.
Figured it out. Though I have yet to find it documented anywhere, default attributes with a space in the name in the ui need to have that space replaced with an underscore when using their name in the api.

UWP: GetFileAsync on a partial path?

I'm trying to get a single file in an UWP based on its partial name. Basically, I want to select a file that starts with "latest_" in a given folder.
Right now I can do that with that command:
var previousInfo = (await rootFolder.GetFilesAsync()).Where(file => file.DisplayName.StartsWith("latest_")).FirstOrDefault();
I suppose this code is not very efficient if there are a lot of files in the folder...
Is there a simpler way to do this? Like:
var previousInfo2 = await rootFolder.GetFileAsync("latest_*");
Or:
var previousInfo2 = await rootFolder.GetFileAsync(#"latest_*");
Right now this code is throwing an exception (invalid parameter). And the MSDN documentation about this function is very light: it just says that the function takes a string as parameter...
Thanks!
You can do this by using Advanced Query Syntax and in fact, you can filter/search by many more file metadata. Below is an example for search based on File Name.
var queryOptions = new QueryOptions();
queryOptions.ApplicationSearchFilter = "System.FileName:latest_*";
StorageFileQueryResult queryResult = rootFolder.CreateFileQueryWithOptions(queryOptions);
var files = await queryResult.GetFilesAsync();

Calling javascript object method using WebBrowser.Document.InvokeScript

In my WinForms application I need to call javascript function from my WebBrowser control. I used Document.InvokeScript and it works perfect with functions alone e.g
Document.InvokeScript("function").
But when i want to call javascript object method e.g.
Document.InvokeScript("obj.method")
it doesn't work. Is there a way to make it work? Or different solution to this problem? Without changing anything in the javascript code!
Thanks in advance :)
The example in the documentation does NOT include the parenthesis.
private void InvokeScript()
{
if (webBrowser1.Document != null)
{
HtmlDocument doc = webBrowser1.Document;
String str = doc.InvokeScript("test").ToString() ;
Object jscriptObj = doc.InvokeScript("testJScriptObject");
Object domOb = doc.InvokeScript("testElement");
}
}
Try
Document.InvokeMethod("obj.method");
Note that you can pass arguments if you use HtmlDocument.InvokeScript Method (String, Object[]).
Edit
Looks like you aren't the only one with this issue: HtmlDocument.InvokeScript - Calling a method of an object . You can make a "Proxy function" like the poster of that link suggests. Basically you have a function that invokes your object's function. It's not an ideal solution, but it'll definitely work. I'll continue looking to see if this is possible.
Another post on same issue: Using WebBrowser.Document.InvokeScript() to mess around with foreign JavaScript . Interesting solution proposed by C. Groß on CodeProject:
private string sendJS(string JScript) {
object[] args = {JScript};
return webBrowser1.Document.InvokeScript("eval",args).ToString();
}
You could make that an extension method on HtmlDocument and call that to run your function, only using this new function you WOULD include parenthesis, arguments, the whole nine yards in the string you pass in (since it is just passed along to an eval).
Looks like HtmlDocument does not have support for calling methods on existing objects. Only global functions. :(
Unfortunately you can't call object methods out of the box using WebBrowser.Document.InvokeScript.
The solution is to provide a global function on the JavaScript side which can redirect your call. In the most simplistic form this would look like:
function invoke(method, args) {
// The root context is assumed to be the window object. The last part of the method parameter is the actual function name.
var context = window;
var namespace = method.split('.');
var func = namespace.pop();
// Resolve the context
for (var i = 0; i < namespace.length; i++) {
context = context[namespace[i]];
}
// Invoke the target function.
result = context[func].apply(context, args);
}
In your .NET code you would use this as follows:
var parameters = new object[] { "obj.method", yourArgument };
var resultJson = WebBrowser.Document.InvokeScript("invoke", parameters);
As you mention that you cannot change anything to your existing JavaScript code, you'll have to inject the above JavaScript method in some how. Fortunately the WebBrowser control can also do for you by calling the eval() method:
WebBrowser.Document.InvokeScript("eval", javaScriptString);
For a more robust and complete implementation see the WebBrowser tools I wrote and the article explaining the ScriptingBridge which specifically aims to solve the problem you describe.
webBrowser.Document.InvokeScript("execScript", new object[] { "this.alert(123)", "JavaScript" })
for you supposed to be like this
webBrowser.Document.InvokeScript("execScript", new object[] { "obj.method()", "JavaScript" })

Categories

Resources