Cannot toggle fullscreen in testcafe - javascript

I have a react page in an iframe with a button that can enter fullscreen mode. I've tested this manually with firefox and chrome which both work. However, clicking with testcafe does not go to fullscreen mode and fails the test.
I'm assuming the iframe isn't the issue because it works manually but wanted to mention it just in case.
This is some of the app:
fs.fullScreen = function () {
const el = (fsEl && fsEl.current) || document.documentElement
if (el.requestFullscreen) return el.requestFullscreen()
if (el.mozRequestFullScreen) return el.mozRequestFullScreen()
if (el.webkitRequestFullscreen) return el.webkitRequestFullscreen()
if (el.msRequestFullscreen) return el.msRequestFullscreen()
}
<button onClick={fs.fullScreen}>Fullscreen</button>
This is the test that fails:
beforeEach((t) => t.switchToIframe(storybook.iframe))
afterEach((t) => t.switchToMainWindow())
test('The Fullscreen button enters fullscreen mode', async (t) => {
const { description, fullscreenButton } = storybook.hooks.fullscreen
await t
.expect(description.textContent)
.contains('Browser not in fullscreen mode')
.click(fullscreenButton)
.expect(description.textContent)
.contains('Browser in fullscreen mode')
})
In Firefox I've seen the console error: Request for fullscreen was denied because Element.requestFullscreen() was not called from inside a short running user-generated event handler.
In Chrome, I've seen the console error: Uncaught TypeError: fullscreen error.
The firefox error is fairly clear but I don't know how to work around it with testcafe. There is a way by editing the firefox profile before running the test but that won't work for chrome or other browsers. Is the chrome issue likely to be for the same reason?
This seems like it would be a common issue that automation frameworks have solved, so I might be missing something obvious?

I was able to reproduce the issue in the simple example:
Test page:
<button id="btn">click me</button>
<script>
var button = document.getElementById('btn');
button.addEventListener('click', function () {
button.requestFullscreen();
});
</script>
Test code
test(`test`, async t => {
await t.click('#btn');
});
I'm not sure whether this functionality will be supported or not, since we need to research it in detail.
As a workaround, you can mock the requestFullscreen method on the target element. It will suppress the error, but won't bring your page to the fullscreen layout. Check the following code as a reference:
const mockFullcreen = ClientFunction(element => element.requestFullscreen = () => {});
await mockFullcreen(target);
await t.click(target);

Related

Testcafe: Scripts are working fine in Firefox browser but are failing in chrome browser because testcafe is not able to scroll

Testcafe Scripts are working fine over Firefox browser but failing over chrome browser. The reason which we identified is that 'when application is open on Firefox browser testcafe itself scroll down to that element and perform action on that element. But in chrome testcafe itself does not able to scroll down and throws error " The specified selector does not match any element in the DOM tree". We even tried to forcefully scroll down till the element but it didn't work.
The following testcafe function we have tried are mentioned below:-
ScrollBy
ScrollIntoView
Scroll
Keyevent
hover
mouse down event
The code Snippet we tried are mentioned below :
1. const scrollBy = ClientFunction((x, y) => { window.scrollBy(x, y);
}); const targetElementPosition = Selector('#id'); await
scrollBy(0, targetElementPosition);
2.const target = Selector('#id'); await t.scrollIntoView(target);
3. await t.scrollBy(0, #id);
4. await t.scrollIntoView(#id)
5. await t.pressKey('pagedown')
6. await t.hover('#id')
7. const target =Selector('#id'); await t.scrollIntoView(target);
8. var el = Selector(document.getElementById(id)); el.scrollIntoView(true);
9. const target = Selector('#target');
await t
.dispatchEvent(target, 'mousedown')
.wait(5000)
please suggest solution for this issue. Any help would be appreciated. Thank you in advance!!
Scroll TestCafe works correctly in Chrome. Here is an example that demonstrates this:
fixture `New Fixture`
.page `http://devexpress.github.io/testcafe/example/`;
test(`New Test`, async t => {
await t
.resizeWindow(360, 240)
.hover('#tried-test-cafe')
.wait(2000);
});
So, the issue is that TestCafe can't find the matching element in the DOM. Please share an example of a page that reproduces the incorrect behavior.
The overlay value is invalid for the overflow-y property. Chrome treats it as auto and Firefox treats it as an error. The scroll functions don't work because Firefox doesn't consider the element's content to be scrollable. If you think that this is not a problem for your application in Firefox, and for the user everything looks fine (although it probably doesn't and it's better to fix it in the application), then you can try replacing the overflow-y style with ClientFunction:
const setStyle = ClientFunction((selector, key, value) => {
document.querySelector(selector).style[key] = value;
})

Cypress error when testing nested iframes in headless mode - race condition

I am testing a web app and the test runs reliably in headed mode (cypress open) but has errors in headless mode (cypress run), so it's likely a race condition that I cannot resolve. The error message is:
[36819:0223/163815.745047:ERROR:system_services.cc(34)] SetApplicationIsDaemon: Error Domain=NSOSStatusErrorDomain Code=-50 "paramErr: error in user parameter list" (-50)
This error is mentioned again when Cypress creates a video of the incident:
- Started processing: Compressing to 32 CRF
2022-02-23 17:00:19.700 Cypress Helper[37571:416134] In -[NSApplication(NSQuietSafeQuit) _updateCanQuitQuietlyAndSafely], _LSSetApplicationInformationItem(NSCanQuitQuietlyAndSafely) returned error -50
- Finished processing: /Users/malte.wirz/Documents/iframes-cypress-issue/cypress/videos/iframe-github.js.mp4 (3 seconds)
I created a demo repository here. To reproduce, clone it, run yarn to install, and yarn cypress:run. The test does pass, but with the error mentioned above.
I assume that the error stems from accessing the nested iframes and I tested 5 different approaches so far, but to no avail. I especially made sure that the function getIframeBody waits until each iframe and the requested element is ready. The error also creates a video, but you can only see the successful run, the error message is not visible there.
Any help on how to debug this further is much appreciated.
describe('Testing Iframe within Iframes', () => {
it('Visits the iframe website and accesses the iframe within the iframe', () => {
const getIframeBody = (iframeSelector, elementSelectorInIframe) => {
return cy
.get(iframeSelector)
.its('0.contentDocument.body', {timeout: 30000})
.should((body) => {
expect(Cypress.$(body).has(elementSelectorInIframe).length).gt(0)
})
.then(cy.wrap)
}
// Visiting the page index.html and getting iframe A
cy.visit('index.html').contains('XHR in iframe')
getIframeBody('iframe[data-cy="bankid"]', 'iframe[src="https://tools.bankid.no/bankid-test/auth"]').as('iframeA')
cy.get('#iframeA').within(() => {
getIframeBody('iframe[src="https://tools.bankid.no/bankid-test/auth"]', 'iframe[src^="https://csfe.bankid.no/CentralServerFEJS"]').as('iframeB')
cy.get('#iframeB').within(() => {
getIframeBody('iframe[src^="https://csfe.bankid.no/CentralServerFEJS"]', 'input[type="tel"]').as('iframeC')
// Now we are in the right place and it finds the correct input element.
// However, normal cypress command .type() fails and we have to use library cypress-real-events,
// which provides an event firing system that works literally like in puppeteer
cy.get('#iframeC').find('input[type="tel"]').should('be.visible').realType('12345678912')
// But for the button below, this library now doesn't help anymore:
// "Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'."
// This was solved by using {scrollBehavior:false}.
cy.get('#iframeC').find('button[type="submit"]').should('be.visible').first().realClick({scrollBehavior:false})
})
})
})
})
I got some feedback that the above "ERROR:system_services.cc(34)" is not critical and does not cause flaky or unsuccessful tests, therefore there are no action points.

Cypress Iframe Handling - Failure to interact with Button

I am trying to E2E test an auth flow with Cypress that includes a third party method called BankID. BankId is integrated through three nested iframes that I can successfully access. However, when I type into the input field via cy.type('12345678912'), BankId does not register this as trusted events and never unlocks the submit button with the arrow.
According to this issue here, Cypress does not intend to support native browser events and suggests to use the package cypress-real-events. When using this via cy.realType('12345678912'), it actually succeeds in unlocking the submit button. However i can never successfully click the submit button, neither with .click() or even the package method .realClick().
The error is: "Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'."
I uploaded a sample repository with an minimal testing version here.
Any feedback or hints would be greatly appreciated :)
Here is the relevant code:
/// <reference types="cypress" />
import { skipOn } from '#cypress/skip-test'
describe('Recipe: blogs__iframes', () => {
skipOn('firefox', () => {
it('do it more generically', () => {
const getIframeBody = (identifier) => {
return cy
.get(identifier)
.its('0.contentDocument.body')
.should('not.be.empty')
.then(cy.wrap)
}
// Visiting the page index.html and getting iframe A
cy.visit('index.html').contains('XHR in iframe')
getIframeBody('iframe[data-cy="bankid"]').as('iframeA')
cy.get('#iframeA').within(() => {
getIframeBody('iframe[src="https://tools.bankid.no/bankid-test/auth"]').as('iframeB')
cy.get('#iframeB').within(() => {
getIframeBody('iframe[src^="https://csfe.bankid.no/CentralServerFEJS"]').as('iframeC')
// Now we are in the right place and it finds the correct input element.
// However, normal cypress command .type() fails and we have to use library cypress-real-events,
// which provides an event firing system that works literally like in puppeteer
cy.get('#iframeC').find('input[type="tel"]').should('be.visible').realType('12345678912')
// But for the button below, this library now doesn't help anymore:
// "Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'."
cy.get('#iframeC').find('button[type="submit"]').should('be.visible').first().realClick()
})
})
})
})
})
I also posted this problem on https://github.com/dmtrKovalenko/cypress-real-events/issues/226 and got an answer there:
Using .realClick({ scrollBehavior: false }); solved the issue.
The problem is if the webapp is not scrolling as expected, therefore leading to Cypress not finding the element. In my case, i made the iframe wider to avoid needing to scroll and the issue was still there, but the workaround solved it anyway.

Why doesn't "console.log()" work on this website?

I can open the Chrome DevTools console for this URL: https://www.google.com/ and enter the following command to print "Hello world!" to the console:
console.log("Hello world!")
However, when I attempt to use the same command for this URL: https://svc.mt.gov/dor/property/prc my message isn't printed in the console. Why is that?
Is there any way to force the console to work for this MT website?
I've tried using python/selenium to open the page and execute_script() to issue the command, but that hasn't worked either.
If you do
console.dir(console.log)
and click on the [[FunctionLocation]], you'll see the following in the site's source code:
"Debug" != e && (window.console.log = function() {}
)
Seems like a cheap (but lazy?) way to suppress logging on production.
One way to fix it, kinda, would be to use one of the many other console commands. (console.dir, or console.error, if you want)
If you really want the original console.log to work again, the only way to do that would be to run code before the page code runs, so that you can save a reference to the function. For example, with a userscript:
// <metadata block...>
// #grant none
// ==/UserScript==
const origConsoleLog = console.log;
// set original window.console.log back to its original
// after the page loads
setTimeout(() => {
window.console.log = origConsoleLog;
}, 2000);
// there are more elegant but more complicated approaches too
CertainPerformance already explained how this is possible and what the reason for suppressing the log seems to be. I'm going to show here another method to get back console.log after the original has been lost.
document.body.appendChild(document.createElement('IFRAME')).onload = function () {
this.contentWindow.console.log('Hello, World!');
this.remove();
};
This basically uses an old method of restoring global objects from a temporary iframe.

DOMException on calling navigator.clipboard.readText()

Following lines of code used to work and stopped working after chrome upgrade to Version 74.0.3729.169 (Official Build) (64-bit). Now I get DOMException even though permission is set correctly. Appreciate if you can explain what is the bug and workaround. Exception details:
message:Document is not focused
name:NotAllowedError
code:0
navigator.permissions.query({ name: 'clipboard-read' }).then(result => {
// If permission to read the clipboard is granted or if the user will
// be prompted to allow it, we proceed.
if (result.state === 'granted' || result.state === 'prompt') {
navigator.clipboard.readText()
.then(text => {
//my code to handle paste
})
.catch(err => {
console.error('Failed to read clipboard contents: ', err);
});
}
}
This seems to happen when executing code from the devtools console or snippets.
Workaround:
You can execute the code below and focus on the window within 3 seconds, by clicking somewhere, or just by pressing <tab>.
e.g. from snippets
Ctrl-Enter
<Tab>
e.g. from console
Enter
<Tab>
setTimeout(async()=>console.log(
await window.navigator.clipboard.readText()), 3000)
The issue I was having was that I had an alert to say that the text had been copied, and that was removing focus from the document. Ironically, this caused the text to not be copied. The workaround was quite simple:
clipboard.writeText(clippy_button.href).then(function(x) {
alert("Link copied to clipboard: " + clippy_button.href);
});
Just show the alert when the Promise is resolved. This might not fix everybody's issue but if you came here based on searching for the error this might be the correct fix for your code.
As Kaiido said, your DOM need to be focused. I had the same problem during my development when i put a breakpoint in the code... The console developper took the focused and the error appear. With the same code and same browser, all work fine if F12 is closed
Problem
It's a security risk, clearly. :)
Solution
I assume you face this when you are trying to call it from the dev tools. Well, to make life easier, I am taking Jannis's answer, to a less adrenaline-oriented way. :)
I am adding a one-time focus listener to window to do the things magically after hitting "tab" from the Devtools.
function readClipboardFromDevTools() {
return new Promise((resolve, reject) => {
const _asyncCopyFn = (async () => {
try {
const value = await navigator.clipboard.readText();
console.log(`${value} is read!`);
resolve(value);
} catch (e) {
reject(e);
}
window.removeEventListener("focus", _asyncCopyFn);
});
window.addEventListener("focus", _asyncCopyFn);
console.log("Hit <Tab> to give focus back to document (or we will face a DOMException);");
});
}
// To call:
readClipboardFromDevTools().then((r) => console.log("Returned value: ", r));
Note: The return value is a Promise as it's an asynchronous call.
if you want to debug a and play around to view result. also can hide this <p></p>.
async function readClipboard () {
if (!navigator.clipboard) {
// Clipboard API not available
return
}
try {
const text = await navigator.clipboard.readText();
document.querySelector('.clipboard-content').innerText = text;
} catch (err) {
console.error('Failed to copy!', err)
}
}
function updateClipboard() {
// Here You Can Debug without DomException
debugger
const clipboard = document.querySelector('.clipboard-content').innerText;
document.querySelector('.clipboard-content').innerText = 'Updated => ' + clipboard;
}
<button onclick="readClipboard()">Paste</button>
<p class="clipboard-content"></p>
<button onclick="updateClipboard()">Edit</button>
As the exception message says, you need to have the Document actively focused in order to use this API.
Suppose there is a p element. you want to copy its innerText.
So, lets not use navigation.clipboard (because 0f the error your are facing)
So below given is a example which copies the innerText of the p element when that button is clicked. your do not to rely upon "clicking" the button manually by using the code below. you can trigger the "click" by executing code like pElement.click() from devtools console.
Your devtools console problem, that #jannis-ioannou mentioned in his post above, will not occur!
function myFunction() {
var copyText = document.getElementById("copy-my-contents");
var range = document.createRange();
var selection = window.getSelection();
range.selectNodeContents(copyText);
selection.removeAllRanges();
selection.addRange(range);
document.execCommand("copy");
}
<p id="copy-my-contents">copy-me</p>
<button onclick="myFunction()">Copy text</button>
I was facing this in a Cypress test, this fixed it for me:
first focus the copy icon/button which is about to be clicked, then
use cy.realClick from cypress-real-events instead of cy.click
I just discovered that I don't need to write any code to debug this. When you pause on a breakpoint in Chrome DevTools, it adds a small yellow box to the page you're debugging which says, "Paused in debugger" and has play and step over buttons. I've never used it before, preferring the more extensive controls in DevTools.
I just discovered that if you use the step over button in this yellow box, the DOM stays focused and no error is thrown.
Cypress - Use the right window/navigator and focus on the document.
I tried to programatically populate the clipboard in my Cypress test so I could paste the contents into a text-area input element.
When I was struggling with this issue I found out that there were two things causing the problem.
The first issue was that I used the wrong window. Inside the test function scope window returns the Window object in test scope, while cy.window() returns the Window object for the Application Under Test (AUT).
Second issue was that document was not in focus, which can be easily resolved by calling cy.window().focus();.
Since both result in the same DOMException:
NotAllowedError: Document is not focused.
It was not always clear that there were 2 issues going on. So when debugging this:
Make sure you use the right window/navigator of the page you are testing.
Make sure that the document is focused on.
See following Cypress tests demonstrating the above:
describe('Clipboard tests', () => {
before(() => {
cy.visit('/');
// Focus on the document
cy.window().focus();
});
it('window object in test scope is not the window of the AUT', () => {
cy.window().then((win) => {
expect(window === win).to.equal(false);
expect(window.navigator === win.navigator).to.equal(false);
})
});
it('See here the different results from the different Window objects', () => {
expect(window.navigator.clipboard.writeText('test').catch(
(exception) => {
expect(exception.name).to.equal('NotAllowedError')
expect(exception.message).to.equal('Document is not focused.')
},
));
cy.window().then((win) => {
return win.navigator.clipboard.writeText('test').then(() => {
return win.navigator.clipboard.readText().then(
result => expect(result).to.equal('test'),
);
});
});
})
});
We saw a handful of these in our production app, and after some digging it turned out that the root cause in our case was the user was copying a lot of data, and switched tabs during the process (thus causing the relevant DOM to lose focus, and triggering the error). We were only able to replicate this by using CPU throttling in chrome, and even then it doesn't happen every time!
There was nothing to be "fixed" in this case - instead we're just catching the error and notifying the user via a toast that the copy failed and that they should try again. Posting in case it's of use to anyone else seeing this!
If you're using Selenium and getting this, you need to bring the test window to the front:
webDriver.switchTo().window(webDriver.getWindowHandle());
I have to do this repeatedly so I have it in a loop with a Thread.sleep() until the the paste works.
Full details: https://stackoverflow.com/a/65248258/145976

Categories

Resources