PlayWright: Page.title() returning empty value. Trying to add automation in electron-vue project - playwright

For automated testing, I'm attempting to incorporate Playwright into my electron-vue project. I run the test, and Page.title() returns "". Here is the code:
test("renders the screen splash", async () => {
let page: Page;
page = await electronApp.firstWindow();
console.log("Title: ", await page.title());
const title = await page.title()
expect(title).toBe('Splash')
});

Could you try if it helps?
test("renders the screen splash", async () => {
let page: Page;
page = await electronApp.firstWindow();
// add the following line
await page.waitForLoadState();
console.log("Title: ", await page.title());
const title = await page.title()
expect(title).toBe('Splash')
});

Related

Electron desktop Application testing with[PLAYWRIGHT]

I'm attempting to use playwright to automate an electron js application, but I can't seem to find any relevant information. To automate a simple programme, I used playwright:- https://playwright.dev/docs/api/class-electron and https://www.electronjs.org/docs/latest/tutorial/quick-start. However, I am unable to obtain the elements (buttons, dropdowns, and so on) in the electron application. Any reference or documentation that will deeply guide me to automate desktop application using playwright.
I got mine to work using their intro guide
for me since the installer installs additional components, i had to build and install, then supply the path to the exe
in my package.json i have.
"playwright": "^1.25.0",
"#playwright/test": "^1.25.0",
"eslint-plugin-playwright": "^0.10.0",
I created this class to help me have a cleaner code.
import { _electron as electron, ElectronApplication, Page } from 'playwright';
class ElectronAppController {
static electronApp: ElectronApplication;
static window1: Page;
static window2: Page;
static window3: Page;
static async launchApp() {
ElectronAppController.electronApp = await electron.launch({
executablePath: 'C:\\pathTo\\app.exe',
});
ElectronAppController.electronApp.on('window', async (page) => {
ElectronAppController.assignWindows(page);
});
const mywindows: Page[] =
await ElectronAppController.electronApp.windows();
for (
let index = 0, l = mywindows.length;
index < l;
index += 1
) {
ElectronAppController.assignWindows(
mywindows[index]
);
}
}
private static assignWindows(page: Page) {
const myurl = path.basename(page.url());
if (myurl === 'window1.html') {
ElectronAppController.window1= page;
}
if (myurl === 'window2.html') {
ElectronAppController.window2= page;
}
if (myurl === 'window3.html') {
ElectronAppController.window3= page;
}
return true;
}
}
the test file name should be [name].spec.ts, don't forget to import
test.describe('First Window Tests', async () => {
test.beforeAll(async () => {
await ElectronAppController.launchApp();
});
test('Check if first window opened', didLaunchApp);
test('name of the test', async () => {
// test body
// this will allow you to record a test very useful, but sometimes it has some problems check note bellow
await ElectronAppController.window1.pause;
});
test.afterAll(async () => {
await ElectronAppController.electronApp.close();
});
});
here is a didLaunchApp just as a simple test
const didLaunchApp = async () => {
const isVisible: boolean = await ElectronAppController.electronApp.evaluate(
async ({ BrowserWindow }) => {
const mainWindow = BrowserWindow.getAllWindows()[0];
const getState = () => mainWindow.isVisible();
return new Promise((resolve) => {
if (mainWindow.isVisible()) {
resolve(getState());
} else {
mainWindow.once('ready-to-show', () => {
setTimeout(() => resolve(getState()), 0);
});
}
});
}
);
await expect(isVisible).toBeTruthy();
};
you can record tests but sometimes that might make some problems if you have some popups or other effects on hovering over an element,
you can read more about selectors here
I'm just finishing a series of e2e tests using the same as you, Electron with React. What you don't see? Does it at least load the application?
Share the code of one test and how you launch using .launch method.

How can I set up localStorage keys with storageState

I'm trying to set up localStorage when a user registers,
but it only generates the file with no key, values.
If I run npx playwright codegen --save-storage=formsData.json
works fine and generates the key,values but the generated code is very different
and I don't see how localStorage is created.
What I'm doing wrong, or not doing ?
This is my test code:
const { test, expect } = require('#playwright/test');
const { buildUser } = require('./utils/generateUser');
test.describe('Register Form', () => {
test('displays register form and can register user', async ({ browser }) => {
const user = await buildUser();
const page = await browser.newPage();
await page.goto('http://localhost:3000/register');
await expect(page).toHaveURL('http://localhost:3000/register');
const firstNameInput = page.locator('[placeholder="Nombre"]');
const lastNameInput = page.locator('[placeholder="Apellidos"]');
const emailInput = page.locator('[placeholder="Email"]');
const passwordInput = page.locator('[placeholder="Contraseña"]');
const repeatPasswordInput = page.locator('[placeholder="Repite la contraseña"]');
const registerButton = page.locator('text=Adelante');
const termsCheckbox = page.locator('input[type="checkbox"]').first();
const privacyCheckbox = page.locator('input[type="checkbox"]').last();
const modalWindow = page.locator('.styles__ContentWrapper-n48cq5-0');
const modalButton = page.locator('text=Aceptar');
await expect(firstNameInput).toBeEmpty();
await expect(lastNameInput).toBeEmpty();
await expect(emailInput).toBeEmpty();
await expect(passwordInput).toBeEmpty();
await expect(repeatPasswordInput).toBeEmpty();
await expect(registerButton).toBeDisabled();
await expect(termsCheckbox).not.toBeChecked();
await expect(privacyCheckbox).not.toBeChecked();
await expect(modalWindow).toBeHidden();
await firstNameInput.fill(user.nombre);
await lastNameInput.fill(user.apellido);
await emailInput.fill(user.email);
await passwordInput.fill('12341234');
await repeatPasswordInput.fill('12341234');
await termsCheckbox.check();
await privacyCheckbox.click();
await expect(modalWindow).toBeVisible();
await page.press(':nth-match(input[type="checkbox"], 2)', 'Tab');
await page.press('text=info#coinscrap.com', 'Tab');
await await modalButton.click();
await expect(registerButton).toBeEnabled();
await registerButton.click();
await page.context().storageState({ path: 'formsData.json' });
await browser.close();
});
});
This is what playwright codegen does:
const { test, expect } = require('#playwright/test');
test('test', async ({ page }) => {
// Go to http://localhost:3000/register
await page.goto('http://localhost:3000/register');
// Click [placeholder="Nombre"]
await page.click('[placeholder="Nombre"]');
// Fill [placeholder="Nombre"]
await page.fill('[placeholder="Nombre"]', 'Pascale');
// Click [placeholder="Apellidos"]
await page.click('[placeholder="Apellidos"]');
// Fill [placeholder="Apellidos"]
await page.fill('[placeholder="Apellidos"]', 'Gusikowski');
// Click [placeholder="Email"]
await page.click('[placeholder="Email"]');
// Fill [placeholder="Email"]
await page.fill('[placeholder="Email"]', 'Pascale_Gusikowski86#gmail.com');
// Click [placeholder="Contraseña"]
await page.click('[placeholder="Contraseña"]');
// Fill [placeholder="Contraseña"]
await page.fill('[placeholder="Contraseña"]', '12341234');
// Click [placeholder="Repite la contraseña"]
await page.click('[placeholder="Repite la contraseña"]');
// Fill [placeholder="Repite la contraseña"]
await page.fill('[placeholder="Repite la contraseña"]', '12341234');
// Check input[type="checkbox"]
await page.check('input[type="checkbox"]');
// Click text=1.1 -Decisiones automatizadas, perfiles y lógica aplicada Los datos recogidos me
await page.click('text=1.1 -Decisiones automatizadas, perfiles y lógica aplicada Los datos recogidos me');
// Press End
await page.press('text=You need to enable JavaScript to run this app. Crea una cuenta​​​​​ AdelanteHe l', 'End');
// Click text=Aceptar
await page.click('text=Aceptar');
// Click text=Adelante
await Promise.all([
page.waitForNavigation(/*{ url: 'http://localhost:3000/internal/banks/start' }*/),
page.click('text=Adelante')
]);
});
There's no code where localStorage is created.
I need to do it programmatically.
I've also tried with:
const localStorage = await page.evaluate(() => JSON.stringify(window.localStorage));
fs.writeFileSync('formsData.json', localStorage);
It generates the file but didn't generate keys, values.
localStorage (DOMStorage) is unrelated to the form you submit. When you submit the form, a POST request is typically issued to the server, sending on this data to the backend. It looks like your page has additional JavaScript code that stores these values into localStorage at some point. Your localStorage does not have these values at the time you save it, so you should figure out how to trigger this code on your page and how to wait for it. You can open DevTools and evaluate "localStorage" in console or pick it in the Application tab to see when and why these values make their way into the local storage.

PDFTron webviewer - how to save the whole redacted pdf to server using ASP.net MVC Core

I am currently a developing an application in MVC Core that is using a PDFTron webviewer. Is there anyway to save the edited pdf edited with pdftron webviewer to the server?
There is a feature of pdftron that saves annotations to the server, but I need to save the whole pdf with the edits to the server.
WebViewer({
path: '/lib/WebViewer',
initialDoc: '/StaticResource/Music.pdf', fullAPI: !0, enableRedaction: !0
}, document.getElementById('viewer')).then(
function(t) {
samplesSetup(t);
var n = t.docViewer;
n.on('documentLoaded', function() {
document.getElementById('apply-redactions').onclick = function() {
t.showWarningMessage({
title: 'Apply redaction?',
message: 'This action will permanently remove all items selected for redaction. It cannot be undone.',
onConfirm: function () {
alert( );
t.docViewer.getAnnotationManager().applyRedactions()
debugger
var options = {
xfdfString: n.getAnnotationManager().exportAnnotations()
};
var doc = n.getDocument();
const data = doc.getFileData(options);
const arr = new Uint8Array(data);
const blob = new Blob([arr], { type: 'application/pdf' });
const data = new FormData();
data.append('mydoc.pdf', blob, 'mydoc.pdf');
// depending on the server, 'FormData' might not be required and can just send the Blob directly
const req = new XMLHttpRequest();
req.open("POST", '/DocumentRedaction/SaveFileOnServer', true);
req.onload = function (oEvent) {
// Uploaded.
};
req.send(data);
return Promise.resolve();
},
});
};
}),
t.setToolbarGroup('toolbarGroup-Edit'),
t.setToolMode('AnnotationCreateRedaction');
}
);
When i send the request to the Controller i am not getting the file it is coming null
[HttpPost]
public IActionResult SaveFileOnServer(IFormFile file)
{
return Json(new { Result="ok"});
}
Can any one suggest me where i am going wrong
Thanks in adavance
For JavaScript async function, you need to wait for it completes before doing other things. For example, AnnotationManager#applyRedactions() returns a Promise, the same for AnnotationManager#exportAnnotations() and Document#getFileData().
For JS async functions, you can take a look at:
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
So here you may want to use await to wait for the Promise completes.

Getting value of input element in Playwright

How do I return the value of elem so that I can verify that it is in fact 1?
const elem = await page.$('input#my-input')
await elem.fill('1')
inputValue method has been added in Playwright v1.13.0
await page.inputValue('input#my-input');
Locator:
await page.locator('input#my-input').inputValue();
It returns input.value for the selected <input> or <textarea> element. Throws for non-input elements. Read more.
The easiest way is to use $eval. Here you see a small example:
const playwright = require("playwright");
(async () => {
const browser = await playwright.chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.setContent(`<input id="foo"/>`);
await page.type("#foo", "New value")
console.log(await page.$eval("#foo", el => el.value))
await page.screenshot({ path: `example.png` });
await browser.close();
})();
From version 1.19 (and probably lower versions) Element Handler is not recomended.
Instead of it use Locator.
page.locator(selector).innerText()
in your case with assertion it will be
expect(page.locator("input#my-input").innerText().includes("1")).toBeTruthy()
Read more on:
https://playwright.dev/docs/api/class-elementhandle#element-handle-fill

How to fix slow issue when using async and await for caching using Sqflite?

I store all API data to cache. some APIs have more than 10000 data. Postman response time is within one second. but in application very slow to navigate to next page. I used this code:
onPressed: () async {
...
}
else {
var token = Token(
id: 1,
token: tokens,
refreshToken: model.data.refreshToken,
);
await storeRegister(_url,tokens);
await storeEquipmentReg(_url,tokens);
await storeSyncLogin(_url,tokens);
await HelperDefCatMaster().deleteDefCatMaster();
await storeDefCatMaster(_url,tokens);
await HelperDefRegisterCat().deleteDefRegisterCat();
await storeDefRegisterCat(_url,tokens);
await HelperDefCatMaster().deleteDefCatRelation();
await storeDefCatRelation(_url,tokens);
await HelperDefCatMaster().deleteWoDescription();
await storeWoDescription(_url,tokens);
await HelperDefCatMaster().deleteCategoryDefect();
await storeCategoryDefect(_url,tokens);
await storeWorkSource(_url,tokens);
await storeWorkTypes(_url,tokens);
await storePriorities(_url,tokens);
await Helper().insert(token);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ListPage(model.data.token)));
}
storePriorities function look like,
storePriorities(String url, String token) async {
final response = await http.get(
'${url}/v1.0/Priorities',
headers: {'Authorization': 'Bearer ${token}'},
);
final jsonResponse = json.decode(response.body);
Priorities model = Priorities.fromJson(jsonResponse);
int length = model.data.length;
for (int i = 0; i < length; i++) {
var data = DataPriorities(
i: model.data[i].i,
d: model.data[i].d,
);
await HelperDefCatMaster().insertPriorities(data);
}
}
I have given the first answer that suggests to use await only when it's needed.
Well if you are inserting too much data in SQLite I assume that you might be using something like this:
for (int i = 0; i <= 1000; i++) {
db.insert('table_name', dataObject.toMap());
}
Well this will do a lot many transactions at a time and it will consume a lot's of your time.
Change this to something like this and it will increase the speed of inserting data:
Batch batch = db.batch();
for (int i = 0; i <= 1000; i++) {
batch.insert('table_name', dataObject.toMap());
}
await batch.commit();
What we are doing here is that, in single transaction we are doing multiple inserts at a time.
I made this change in my demo project where I was inserting 1000 row at a time and results were great. db.insert when called 1000 times took 7 secs where as batch.insert took less than 1 sec for inserting same amount of data.
If you optimize your code with this solution and use await when needed you should not face any problem on UI. Let me know if this helps.
You are using await keyword to fetch data from SQLite.
And you are fetching a lots of data.
This will make data fetching synchronous, and will affect your UI.
If it is convenient for your use-case to fetch data asynchronously then you can use the following way:
Change :
await Helper().insert(token);
Navigator.push(
context,MaterialPageRoute(builder: (context) => ListPage(model.data.token)));
to :
Helper().insert(token).then((onValue) {
Navigator.push(context,MaterialPageRoute(
builder: (context) => ListPage(model.data.token),
),
);
}
Note: Make your insert method return Future<'token's return type'>
Now use this way for all other await calls.

Resources