What is the best way to wait for 'WebComponentsReady' event in TestCafe? - testing

I want to wait for web components in the page to upgrade before running any TestCafe tests (In other words, wait for WebComponentsReady event before running tests). What is the best way to do this?

TestCafe starts to execute any action on the page after the DOMContentReady event is raised. As I see, the WebComponentsReady event can be raised before DOMContentReady.
TestCafe allows you to wait for some events in the browser by using ClientFunction:
const waitForWebComponentsReady = ClientFunction(() => {
return new Promise(resolve => {
window.addEventListener('WebComponentsReady', resolve);
});
});
await waitForWebComponentsReady();
However, note that TestCafe can't guarantee that this code will be executed before the WebComponentReady event is raised. As a result, this Promise will not be resolved.
As a solution, you can find another way to identify if the required Web Component is loaded. For example, you can check that some element is visible on the page:
await t.expect(Selector('some-element').visible).ok();
Meanwhile, TestCafe has a feature suggestion to add the capability to execute a custom script before page initialization scripts. You will be able to use code like this when the feature is implemented:
import { ClientFunction } from 'testcafe';
const initScript = `window.addEventListener('WebComponentsReady', () => window.WebComponentsLoaded = true);`;
fixture `My Fixture`
.page `http://example.com`
.addScriptToEachPage(initScript)
.beforeEach(async t => {
await t.expect(ClientFunction(() => window.WebComponentsLoaded)()).ok();
});

Related

Property 'openWindow' does not exist on type 'TestController'

In my test I click on an element and then the new window is opened. But what I need is to check request from the previous window.
I was trying to do this:
https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/openwindow.html
import { Selector } from 'testcafe';
fixture `Example page`
.page `http://www.example.com/`;
test('Open the TestCafe website', async t => {
await t
.openWindow('http://devexpress.github.io/testcafe')
.openWindow('./documentation');
const localWebsite = await t.openWindow('file://path/to/my/website');
});
but I got this error:
Property 'openWindow' does not exist on type 'TestController'.
Your test case works fine in the latest version of TestCafe 1.9.0 where this API introduced.
Make sure that you are using a proper TestCafe version.

Testcafe - Unable to click on a button

I am trying to click on a button but for some weird reason, I am not able to. Here is the code.
import { ClientFunction } from 'testcafe';
import { Selector } from 'testcafe';
fixture `Fixture`
.page `https://viewshape.com/shapes/12b5ntdyuf7`
test(`test`, async t => {
await t.maximizeWindow();
await t.wait(3000);
const fullScreen = Selector('.sp-controls-btn.js-sp-fullscreen-button').filterVisible();
//await t.debug();
await t
.expect(fullScreen.with({visibilityCheck: true}).exists)
.ok({timeout: 30000})
.hover(fullScreen)
.click(fullScreen);
await t.wait(4000);
});
But if I go through debug mode using .debug() and then use Next-Action option in the debugger, the .click() works.
I am not able to understand what is going on here. Why is it .click() is working in .debug() but not in normal flow.
When the Testcafe click action is executed on a DOM element, it calls the element.click() method. Mentioned 'Failed to execute 'requestFullscreen' on 'Element' error means that click event handler calls the requestFullscreen method, which must be called inside a user interaction only. This is a browser's security restriction and there is no way to overcome it.

Testcafe - How do you wait for the `before` hook to complete before loading the page

I am using the test.before hook. My understanding is that this would be completed before the test would load the page.
What I am required to do is a navigateTo as the first action in my test.
test.page("/home").before(async t => {
await setupMockApis()
})("The bank account is added", async t => {
// the mock APIs are not finished setting up
// so I am required to do a navigateTo first
t.navigateTo("/home");
});
Is this the expected behaviour? Is it possible to get the before to complete before the test loads the page?
The common scenario is to login in before hook and it is implied that the page is loaded. In your case, you can avoid unnecessary page load omitting the page function call and navigating to your page in the hook as follows:
test.before(async t => {
await setupMockApis();
await t.navigateTo("/home");
})("The bank account is added", async t => {
});

Setting attribute via a ClientScript not persisting

My fixture has a ClientScript that sets an attribute on an element (the element comes from the script doing a document.querySelectorAll(...)):
element.setAttribute("data-foo", 'foo');
Later in testcafe-land when I try to access that element using a Selector it's always null:
const element = await Selector('[data-foo="foo"]')();
I've stuck a debugger statement into my injected ClientScript and as I step thru the function I see the element does indeed get the attribute added but as soon as I let the execution continue and try to find it again in the devtools Console the attribute is gone from the element.
I -think- it has to do with the iframe/shadow DOM? testcafe is creating? I see there is a sub-iframe/execution context in chrome devtools - my ClientScript is being injected into window.top but there is a testcafe-created iframe in the mix too.
ANY THOUGHTS how I can get this to work? I want an injected script to manipulate the DOM (by adding an attribute) that later on I want a testcafe Selector to select. Why doesn't this work and how can I get it working???
thanks!!!
Mark
TestCafe injects custom scripts into the head tag. These scripts are executed before DOM is loaded.
Here is an example of how to manipulate DOM using the client script:
import { Selector } from 'testcafe';
const scriptContent = `
window.addEventListener('DOMContentLoaded', function () {
var p = document.querySelector('p');
p.setAttribute("data-foo", 'foo');
});
`;
fixture `My fixture`
.page('https://devexpress.github.io/testcafe/example/')
.clientScripts({ content: scriptContent });
test('My test', async t => {
const el = await Selector('[data-foo="foo"]');
await t
.expect(el.textContent).eql('This webpage is used as a sample in TestCafe tutorials.');
});

Cypress - log response data from an request after a click()

Although I know this may not be considered as a best practice, but what I want to achieve is to silently delete a record from a database after the same was created throughout UI. In htat way I want to keep our test environment clear as much as possible and reduce the noise of test data.
After my tests create a new record by clicking over the UI, I wait for POST request to finish and then I would like to extract the id from the response (so I can reuse it to silently delete that record by calling the cy.request('DELETE', '/id')).
Here's a sample test I have put on as a showcase. I'm wondering why nothing is logged in this example.
it('GET cypress and log', () => {
cy.server()
.route('**/repos/cypress-io/cypress*')
.as('getSiteInfo');
cy.visit('https://www.cypress.io/dashboard');
cy.get('img[alt="Cypress.io"]')
.click()
.wait('#getSiteInfo')
.then((response) => {
cy.log(response.body)
})
})
As far as I can see from here https://docs.cypress.io/api/commands/wait.html#Alias this should be fine.
your code contains two problems.
First:
The click triggers a new page to be loaded but cypress does not wait until the PageLoad event is raised (because you do not use visit). On my PC the Request takes about 5 seconds until it is triggered after the click. So you should use wait(..., { timeout: 10000 }).
Second:
wait() yields the XHR object, not the response. So your code within then is not correct. Also the body is passed as object. So you should use JSON.stringify() to see the result in the command log.
This code works:
describe("asda", () => {
it('GET cypress and log', () => {
cy.server()
.route('**/repos/cypress-io/cypress*')
.as('getSiteInfo');
cy.visit('https://www.cypress.io/dashboard');
cy
.get('img[alt="Cypress.io"]')
.click()
.wait('#getSiteInfo', { timeout: 20000 })
.then((xhr) => {
cy.log(JSON.stringify(xhr.response.body))
})
})
})
Instead of route and server method, try intercept directly