Playwright & Accessing sessionStorage - playwright

I have an application that I want to test with some automated UI tests, where the devs have stored some of the user's preferences in the sessionStorage. There appears to be a bug where the session storage isn't getting cleared out when we expect it to be. This is the bug I'm trying to recreate.
After fighting with pure Selenium for a while, I tried Playwright, and I almost have this working, but not quite.
I'm using C# (.NET 6) with Playwright.Nunit 1.27.1.
In my test, the user logs into the site, and after the site is logged into, I want to get the preferences object from the session storage. This is the code I'm using for that section:
// NOW GET SESSION
// sessionStorage.getItem('settingsObj')
var sessionStorage = await Page.EvaluateAsync<string>("() => JSON.stringify( sessionStorage.getItem('settingsObj'))");
Console.WriteLine($"STORAGE? {sessionStorage}");
}
When I run this test, the sessionStorage prints out as a NULL.
However, if I run this test in the debugger, with breakpoints on the var sessionStorage... line and the Console.WriteLine... lines, I see that the sessionStorage DOES contain my object during the run.
So. How do I get this object OUT so I can parse it?

Related

UI5-Application: Call to functionimport works ONLY in WebIDE but fails everywhere else

We are developing a custom UI5 application.
It is developed in the WebIDE, and therefore deployed as a BSP.
When we use the underlying model for calls ( currently 3, no CRUD ), we chose the path of using ONLY functionimports to communicate with the backend.
All of them work with the POST method.
And all of them work ONLY inside the WebIDE.
Once, I access the BSP URL otherwise, we get HTTP 500 error with "error while requesting the ressource.
We already created links, to enable special portfowarding, no result.
Let's stick to my URL from the BSP first.
I paste it into my 3 browsers: 500.
We also created a special non dialogue-user with proper roles and permissions, and in the SICF tree we assigned it .
Again, when calling from inside the WebIDE, the functionimport-calls work, otherwise not.
Error-Logs are empty.
Dumps do not happen.
ST05 trace shows where 500 is passed, deeply inside the HTTP framework, yet no chance to spot the code location, neither a breaktpoint.
In SICF logon-settings we have:
Types all, also flagged "all", SAML: inherited from parent node, sec-sessions Not limited, fix user and pw, sec: Standard, auth:Standard Sap user.
The gui-options contain ONLY one flag: ~CHECK_CSRF_TOKEN 0.
In my client I use :
Where the model is initialized as :
function initModelV2() {
var sUrl = "/sap/opu/odata/sap/Z_this_is_a_company_secret_service/";
var oModel = new sap.ui.model.odata.v2.ODataModel(sUrl);
sap.ui.getCore().setModel(oModel);
}
What else can I do to get "at least closer" to the reason, WHY ?
I could solve it, and believe it or not, sometimes simple logic helps.
I debugged the backend of CL_HTTP_RESPONSE, and once I saw, GET_STATUS, I thought to look for SET_STATUS.
There it was:
this.rModel.setHeaders( {"X-Requested-With" : "X" } );
Was missing.
Though I set it in the manifest of my model, it was not passed.
Once set in the code, it worked.
I wonder, why it is not accepted in manifest.
I have an assumption.
1st: I have this in my manifest ( yellow arrow shows, where i HAD it set up before):
But I also have an instantiation in my code, in servicebindings.js with this code
Can it be, that, in the end, I have accidently created 2 models ?

Playwright: Failed to Read the localStorage property from Window: Access is denied for this document

I have this script to modify localStorage of my browser instance (assuming all definitions are correct). it returns this Error. I have a feeling that this is because Playwright is launching a browser in incognito mode. Is there a way for me to launch browser in normal mode?
Script:
const rdlocalStorage = fs.readFileSync('localstorage.json','utf8');
const deserializedStorage = JSON.parse(rdlocalStorage);
console.log (deserializedStorage);
await page.evaluate((deserializedStorage)=>{
for (const key in deserializedStorage){
localStorage.setItem(key,deserializedStorage[key]);
}
}, deserializedStorage);
Error Message
enter image description here
I don't know why your error happens, but maybe I do have an alternative.
Playwright has an API to restore cookies and local storage for you, without you having to code it.
context.storageState is the method which allows you to capture the cookies, local storage, etc. You can either await and use the return value as object, or (await and) save the storage state to a file.
Restoring the storage state is done when creating a new browser context, by means of the storageState property on the options parameter of the browser.newContext method. You can either pass it a storageState object or a path to a storage state file.
These same objects and files can also be used in the #playwright/test runner.
The only thing which is not stored in storage state is the session storage (the 'brother' of the local storage). According to the Playwright docs it's not used so often for authentication state, so they don't have a 'native' method for saving and restoring it. But they do describe how to deal with session storage in case it's needed.
Not sure if I am correct but using await page.evaluate might not permit you to do what you're trying to do, thus getting that error. Try instead using the following snippet:
await context.addInitScript((deserializedStorage)=>{
for (const key in deserializedStorage){
localStorage.setItem(key,deserializedStorage[key]);
}
}, deserializedStorage);

How can I prevent Chromium from writing to the console?

I'm making a very simple Delphi console application ({$APPTYPE CONSOLE}) with a single TChromiumWindow on the main form. The purpose of the application is to retrieve a webpage, process the HTML and output some JSON to the console. This can not be done using plain HTTP requests due to the nature of the webpage, which requires running some javascript as well.
Everything works as expected, except for one problem. The chromium components output some error messages to the console as well, which makes my JSON invalid! For example, I always get the following two error messages on startup:
[0529/133941.811:ERROR:gpu_process_transport_factory.cc(990)] Lost UI shared context.
[0529/133941.832:ERROR:url_request_context_getter_impl.cc(130)] Cannot use V8 Proxy resolver in single process mode.
Of course the best solution would be to not get any error messages in the first place, but for several reasons (which mostly have to do with company legacy code) I can't for example disable single process mode.
So the next best thing would be to keep these error messages from being printed to the console. I've tried setting
GlobalCEFApp.LogSeverity := LOGSEVERITY_DISABLE;
but that didn't help. Specifying a logfile using GlobalCEFApp.LogFile doesn't help either.
So how can I prevent the Chromium components from writing to the console at all?
The TChromium component provides an OnConsoleMessage event with signature :
TOnConsoleMessage = procedure(Sender: TObject; const browser: ICefBrowser;
const message, source: ustring; line: Integer;
out Result: Boolean) of object;
If you handle this event and set the Result variable to true the message output to the console is suppressed.
Set LogSeverity to LogSeverity.Fatal or n other desired.
var settings = new CefSettings()
{
//By default CefSharp will use an in-memory cache, you need to specify a Cache Folder to persist data
CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CefSharp\\Cache"),
//Set log severity to showup only fatal errors.
LogSeverity = LogSeverity.Fatal,
};
//Autoshutdown when closing
CefSharpSettings.ShutdownOnExit = true;
//Perform dependency check to make sure all relevant resources are in our output directory.
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);

Using Neo4j with React JS

Can we use graph database neo4j with react js? If not so is there any alternate option for including graph database in react JS?
Easily, all you need is neo4j-driver: https://www.npmjs.com/package/neo4j-driver
Here is the most simplistic usage:
neo4j.js
//import { v1 as neo4j } from 'neo4j-driver'
const neo4j = require('neo4j-driver').v1
const driver = neo4j.driver('bolt://localhost', neo4j.auth.basic('username', 'password'))
const session = driver.session()
session
.run(`
MATCH (n:Node)
RETURN n AS someName
`)
.then((results) => {
results.records.forEach((record) => console.log(record.get('someName')))
session.close()
driver.close()
})
It is best practice to close the session always after you get the data. It is inexpensive and lightweight.
It is best practice to only close the driver session once your program is done (like Mongo DB). You will see extreme errors if you close the driver at a bad time, which is incredibly important to note if you are beginner. You will see errors like 'connection to server closed', etc. In async code, for example, if you run a query and close the driver before the results are parsed, you will have a bad time.
You can see in my example that I close the driver after, but only to illustrate proper cleanup. If you run this code in a standalone JS file to test, you will see node.js hangs after the query and you need to press CTRL + C to exit. Adding driver.close() fixes that. Normally, the driver is not closed until the program exits/crashes, which is never in a Backend API, and not until the user logs out in the Frontend.
Knowing this now, you are off to a great start.
Remember, session.close() immediately every time, and be careful with the driver.close().
You could put this code in a React component or action creator easily and render the data.
You will find it no different than hooking up and working with Axios.
You can run statements in a transaction also, which is beneficial for writelocking affected nodes. You should research that thoroughly first, but transaction flow is like this:
const session = driver.session()
const tx = session.beginTransaction()
tx
.run(query)
.then(// same as normal)
.catch(// errors)
// the difference is you can chain multiple transactions:
const tx1 = await tx.run().then()
// use results
const tx2 = await tx.run().then()
// then, once you are ready to commit the changes:
if (results.good !== true) {
tx.rollback()
session.close()
throw error
}
await tx.commit()
session.close()
const finalResults = { tx1, tx2 }
return finalResults
// in my experience, you have to await tx.commit
// in async/await syntax conditions, otherwise it may not commit properly
// that operation is not instant
tl;dr;
Yes, you can!
You are mixing two different technologies together. Neo4j is graph database and React.js is framework for front-end.
You can connect to Neo4j from JavaScript - http://neo4j.com/developer/javascript/
Interesting topic. I am using the driver in a React App and recently experienced some issues. I am closing the session every time a lifecycle hook completes like in your example. When there where more intensive queries I would see a timeout error. Going back to my setup decided to experiment by closing the driver in some more expensive queries and it looks like (still need more testing) the crashes are gone.
If you are deploying a real-world application I would urge you to think about Authentication and Authorization when using a DB-React setup only as you would have to store username/password of the neo4j server in the client. I am looking into options of having the Neo4J server issuing a token and receiving it for Authorization but the best practice is for sure to have a Node.js server in the middle with something like Passport to handle Authentication.
So, all in all, maybe the best scenario is to only use the driver in Node and have the browser always communicating with the Node server using axios...

Unable to click button using NHtmlUnit

I have a unit test which simulates a login on Uber. The goal is to acquire a token that isn't otherwise accessible without an interactive login. I've had this working in the passed but it appears the Uber login experience has changed and now I'm having issues getting NHtmlUnit to successfully submit the login button.
Here's my latest attempt: https://github.com/wadewegner/uber-sdk-for-net/blob/42fd845fe43e11e3153b07d830ed40e3577b1ed3/src/UberSDKForNet.FunctionalTests/Tests.cs#L100
Here's the key area:
var loginFormButtons = loginForm.GetElementsByTagName("button");
Assert.IsNotNull(loginFormButtons);
var loginButton = (HtmlButton)loginFormButtons.First();
Assert.IsNotNull(loginButton);
var loginButtonText = loginButton.AsText();
StringAssert.Contains("sign in", loginButtonText.toLowerCase());
loginButton.SetAttribute("type", "submit");
loginForm.AppendChild(loginButton);
var consentPage = (HtmlPage)loginButton.Click();
Assert.IsNotNull(consentPage);
I know the code is verbose, but unit testing NHtmlUnit is a nightmare. I've also tried a ton of different permutations, but they pretty much all end up with a HtmlButton.
Everything works great until https://github.com/wadewegner/uber-sdk-for-net/blob/42fd845fe43e11e3153b07d830ed40e3577b1ed3/src/UberSDKForNet.FunctionalTests/Tests.cs#L174. What I'd expect is for this to take me to the consent page where I'd either allow or deny the Uber app access to my information. However, it appears to simply reload the original page and consentPage appears to simply be the same as loginPage.
Any NHtmlUnit experts who might have some ideas?
The only thing I'll also point out is that in the past I was able to successfully use HtmlSubmitInput instead of HtmlButton but this appears to be one of the changes made by Uber. Perhaps this is part of the problem?
Well, it's not perfect, but it appears setting the WebClient to FIREFOX_17 instead of CHROME solved it.
var webClient = new WebClient(BrowserVersion.FIREFOX_17);
See here: https://github.com/wadewegner/uber-sdk-for-net/blob/ceb1cdf80cebb31608744c050b649ddd6a75fb7a/src/UberSDKForNet.FunctionalTests/Tests.cs#L107

Resources