How to wait for multiple locators to be visible - playwright

When waiting for a single Locator to be visible this can be with a simple expect().toBeVisible() but how would I wait for multiple and independent locators to be visible at the same time?
In the following example el1 might be initially visible but while waiting for el2 to be visible el2 might no longer be and I would like to ensure that both elements are visible at the same time.
test('visible', async ({page}) => {
const el1 = page.locator('.el1');
const el2 = page.locator('.el2');
await expect(el1).toBeVisible();
await expect(el2).toBeVisible();
});

You can use .toPass() from v1.29 for this which will retry until all assertions pass or the timeout is met:
await expect(async () => {
const el1 = page.locator('.el1');
const el2 = page.locator('.el2');
await expect(el1).toBeVisible();
await expect(el2).toBeVisible();
}).toPass({
timeout: 10000,
});

Alternatively you may also use Promise.all to make sure both elements are visible.
Code example:
const el1 = page.locator('.el1');
const el2 = page.locator('.el2');
await Promise.all([
expect(el1).toBeVisible();
expect(el2).toBeVisible();
]);

Related

Assert all the items emitted by a Stream in order until it is canceled in Dart?

Is there any convenient way to assert all the items emitted by a Stream in order until it is canceled?
If I use:
expectLater(
stream,
emitsInOrder(<String>[
'item1',
'item2',
]),
);
and the Stream emits ['item1', 'item2', 'item3'] , the test won't fail.
The only way I've found so far is the following:
var count = 0;
final expected = ['item1', 'item2', 'item3'];
stream.listen(
expectAsync1(
(final result) {
expect(result, expected[count++]);
},
count: expected.length,
),
);
But it is a bit verbose and not very easy to read. Is there a simpler/more elegant way?
You can collect the items into a list, using toList, then compare it to your own expectation list:
await expectLater(stream.toList(), completion(expected));
This does not handle the case where the stream doesn't close at all (but then, nothing does, you just have to wait for a timeout).
It doesn't catch errors until all events have been emitted, the emitsInOrder approach is better for that. Not shorter, though.
emitsDone can be used if the Stream is closed at some point.
E.g:
test('Test', () async {
final controller = StreamController<String>();
final stream = controller.stream;
final matcher = expectLater(
stream,
emitsInOrder(<dynamic>[
'Item1',
'Item2',
emitsDone,
]),
);
controller
..add('Item1')
..add('Item2')
..add('Item3')
..close();
await matcher;
await controller.close();
});
The test fails with error:
Expected: should do the following in order:
• emit an event that 'Item1'
• emit an event that 'Item2'
• be done
Actual: <Instance of '_ControllerStream<String>'>
Which: emitted • Item1
• Item2
• Item3
x Stream closed.
which didn't be done
As #Irn suggest, a more compact alternative for Streams that complete at some point is using toList:
test('Test', () async {
final controller = StreamController<String>();
final stream = controller.stream;
final matcher = expectLater(stream.toList(), completion(<String>['Item1', 'Item2']));
controller
..add('Item1')
..add('Item2')
..add('Item3')
..close();
await matcher;
await controller.close();
});
If the Stream is never closed, you can add a timeout and check the items that have been emitted in that period:
test('Test3', () async {
final controller = StreamController<String>();
final stream = controller.stream.timeout(const Duration(milliseconds: 200));
final matcher = expectLater(
stream,
emitsInOrder(<dynamic>[
'Item1',
'Item2',
]),
);
controller
..add('Item1')
..add('Item2');
await matcher;
await controller.close();
});

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.

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

Twilio Studio Not Listing Services

I am setting up a Sync Application using Twilio's Sync Library. For some reason, none of the REST API methods seem to work. That is, I cannot get any of the sync methods to console.log() anything via the runtime functions.
I can, however, console.log() plain text.
Here is my code:
exports.handler = function(context, event, callback) {
// 0. Init
// const phoneNumber = event.phoneNumber;
const issueLimit = 3; // CHANGE IF NEEDED
const listName = 'issues';
const twilioClient = context.getTwilioClient();
// 1. List all lists
twilioClient.sync.services(context.SYNC_SERVICE_SID)
.syncLists
.list({limit: 20})
.then(syncLists => syncLists.forEach(s => console.log(s.sid)));
// 2. return true if quota reached
console.log("Got to here");
// 3. return false
callback(null, undefined);
};
The only code that appears to execute is the 'console.log("Got to here");'. I'm also not receiving any error messages.
Any guidance is sincerely appreciated.
When you see .then(), that's a promise, and you can read more about this here https://www.twilio.com/blog/2016/10/guide-to-javascript-promises.html
In other words, the JavaScript engine goes to your steps 2. and then 3. without waiting for 1. to finish. And since you're returning at step 3 with callback(null, undefined); you won't see the logs.
So, you'll have to move callback() inside the .then(), something like this:
exports.handler = function (context, event, callback) {
// 0. Init
// const phoneNumber = event.phoneNumber;
const issueLimit = 3; // CHANGE IF NEEDED
const listName = 'issues';
const twilioClient = context.getTwilioClient();
// 1. List all lists
twilioClient.sync.services(context.SYNC_SERVICE_SID)
.syncLists
.list({ limit: 20 })
.then(
function (syncLists) {
console.log("Got to here");
syncLists.forEach(s => console.log(s.sid));
callback(null, undefined);
}
);
};

How to use/consume an event stream from wolkenkit-eventstore

I want to use wolkenkit's eventstore and was trying to set up a quick example. But I'm not able to simply output an event stream.
Simplified example:
const eventstore = require("wolkenkit-eventstore/inmemory");
const Stream = require("stream");
const uuidv4 = require("uuid/v4");
const Event = require("commands-events/dist/Event");
const main = async () => {
await eventstore.initialize();
const aggregateId = uuidv4();
const event = new Event({ ... });
event.metadata.revision = 1;
await eventstore.saveEvents({ events: event });
const writableStream = new Stream.Writable();
writableStream._write = (chunk, encoding, next) => {
console.log(chunk.toString());
next()
};
const readableStream = eventstore.getUnpublishedEventStream();
readableStream.pipe(writableStream);
};
main();
As far as I understand, getUnpublishedEventStream returns a readable stream. I followed this instructions, but it didn't work as expected.
All I get is the following error:
(node:10988) UnhandledPromiseRejectionWarning: TypeError: readableStream.pipe is not a function
According to the documentation of wolkenkit-eventstore, getUnpublishedEventStream is an async function, i.e. you have to call it with await. Otherwise, you don't get a stream back, but a promise (and a promise doesn't have a pipe function).
So, this line
const readableStream = eventstore.getUnpublishedEventStream();
should be:
const readableStream = await eventstore.getUnpublishedEventStream();
I have not taken a closer look at your code apart from this, but this is the reason why you get the current error message.
PS: Please note that I am one of the core developers of wolkenkit, so please take my answer with a grain of salt.

Resources