Testcafe - run ClientFunction code on iframe - testing

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

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.

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

Invalid first argument. It must be a callback function

I am new to jest with selenium automation test. I am trying to add a beforeAll() and afterAll() functions to open and close the browsers once and run all the tests across multiple files, instead of calling it individually in all the files and opening multiple browsers and loading the website everytime.
Here is my test:
enter image description here
Output
This is beforeAll() and afterAll() methods sitting in a separate file
Actual Test
beforeAll is different from it or describe. The first argument can only be empty or with done if you using callback:
// works
beforeEach((done) => {
...
done()
});
// works
beforeEach(() => {
return asyncSomething()
});
// Doesn't work.
beforeEach(("Don't add string here") => {..});

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

Changing the webdriver context to 'WEBVIEW'

We're using Protractor and Appium to write tests for a hybrid native application.
I'm having intermittent trouble with the 'autowebview' appium capability. I suspect it is a race condition between a slow emulator changing contexts and protractor sending commands only supported by a webview context.
What I'd like to do is manually set the context to WEBVIEW, in Protractor's onPrepare callback, thanking that this will cut off the race condition described above.
There are code snippets out there like the following that switch the context:
onPrepare: function () {
var wd = require('wd'),
wdBridge = require('wd-bridge')(protractor, wd),
_ = require('underscore');
wdBridge.initFromProtractor(exports.config);
return wdBrowser.contexts().then(function (ctxs) {
var webCtx = _(ctxs).find(function (ctx) {
return ctx.match(/WEBVIEW/);
});
return wdBrowser.context(webCtx)
});
}
However, in my case 'wdBrowser.contexts' is undefined. 'wdBrowser' is a global created by wdBridge; it's defined, but that function is undefined on it, and there's no reference to 'contexts' in the wd-bridge repo. I see that it is a defined command in wd, but I can't find a reference in the WebDriverJS API docs.
Interestingly, it's clearly defined by WebdriverIO, in it's appium section. I'm not quite ready to give up on protractor and wd yet though.
Thanks for any help.