Setting attribute via a ClientScript not persisting - testing

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.');
});

Related

Testcafe - run ClientFunction code on iframe

I need to access a public object available on an iframe scope but the code that I'm running on the ClientFunction gets executed on the parent, I managed to get it working using the --disable-web-security flag and accesing the frame like this window.frames['0'].store. (Not happy with that hack TBH)
But now looks like TestCafe updated to some newer version of Chromium and there's a message telling me that the flag is not allowed anymore.
Is there any way to run client code targetting a specific iframe without needing that nasty flag?
To run ClientFunction on an iframe, you need to switch to it beforehand.
const fn = ClientFunction(() => true);
test('test', async t => {
await t.switchToIframe('#iframe');
await fn();
});

Does TestCafe support multiple tab testing

I have a scenario where clicking on a button opens a new tab but when I try with testCafe it opens in a new window instead of new tab.Why is this ? Doesn’t testCafe support new tab scenarios?
I think using multiple tabs is not really necessary. Usually people try to test that a link opens up in a new tab, but why?
<a class="c-cookie-bar__text-link" target="_blank" href="/private-data-info">More information</a>
You know that a link with target="_blank" will open up in a new tab. This is nothing that would be programmed by your team, this is how a browser works. Just every imaginable browser behaves the same if it encounters this attribute in a link. It's been tested, you don't need to retest it on your website.
However, you still might need to test that the target content loads or that the link looks the way you want. That's ok, but you don't need multiple tabs for that. The following scenarios make sense:
check that the link has the target="_blank" attribute:
test
('Check Target Attr', async t => {
await t
.expect(Selector('.c-cookie-bar__text-link').withAttribute('target', '_blank').exists)
.ok();
});
check that the href attr is what it should be:
test
('Check Href Attr', async t => {
await t
.expect(Selector('.c-cookie-bar__text-link').getAttribute('href'))
.eql('/private-data-info');
});
check that the content loads in the same tab:
import getBaseUrl from '../Helpers/baseUrl';
import { Selector } from 'testcafe';
const baseUrl = getBaseUrl();
fixture `Link Handling`
.page(baseUrl);
test
('Go To Resource', async t => {
const resource = await Selector('.c-cookie-bar__text-link')
.getAttribute('href');
await t
.navigateTo(baseUrl + resource);
// possibly some other assertions follow
});
check the http status code (I use axios here, but it's not all visible in my example):
import getBaseUrl from '../Helpers/baseUrl';
import { Selector } from 'testcafe';
import request from '../Helpers/networkRequest';
const baseUrl = getBaseUrl();
fixture `Link Handling`
.page(baseUrl);
test
('Check Status Code', async t => {
const resource = await Selector('.c-cookie-bar__text-link')
.getAttribute('href');
const networkReq = await request({
method: 'GET',
url: baseUrl + resource
});
await t
.expect(networkReq.status).eql(200);
});
If you actually ignore all this and just click the link in TestCafe:
await t
.click(Selector('.c-cookie-bar__text-link'));
it will be opened in a new window like so:

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.

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

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();
});