I ran into failures using Protractor to test for the visibility of "success toast" type messages which are displayed via PrimeNG Toast. Usually, the messages showed as visible, but every now and then, the call to ExpectedConditions.VisibilityOf would fail with
Failed: Not visible after 20000: By(css selector, .ui-toast-detail)
Wait timed out after 20006ms
It seemed to fail whenever I wasn't looking. As long as I was carefully watching the test execution, the message would always display and the test would pass, but if I started working on something else while letting it run in the background, then suddenly it started to fail. Upon further investigation, I discovered that the Document has a property called VisibilityState which changes between 'visible' and 'hidden', depending on whether some part of the document is actually being displayed on the screen. https://www.w3.org/TR/page-visibility-2/
partial interface Document {
readonly attribute boolean hidden;
readonly attribute VisibilityState visibilityState;
attribute EventHandler onvisibilitychange;
};
I assume that PrimeNG Toast looks at this visibilityState and/or hidden property on the DOM and it simply doesn't bother to render its toast message if the VisibilityState is 'hidden' and if 'hidden' is 'true'
Since I want my tests to be as reliable as possible in any windowed environment — I also want to be able to run my tests while tabbing away to full-screen apps like Slack — I began to search for a way to force the visibilityState even though some other window may be displayed on top of my browser window.
Given that these Document properties are readonly, is there any way modify them from Selenium?
Or is there a way to at least simulate a visibilityState of true so that components which are sensitive to visibilityState will behave as if the document is visible?
It seems like there is an answer here https://sqa.stackexchange.com/questions/32152/force-a-browsers-visibility-setting-to-true
Object.defineProperty(document, 'visibilityState', {value: 'visible', writable: true});
Object.defineProperty(document, 'hidden', {value: false, writable: true});
document.dispatchEvent(new Event("visibilitychange"));
...but I have not yet been able to get it to work. If I run it with executeScript then it does not have any effect and if I run it with executeAsyncScript then it times out.
Update: I have tried the following and many variants on it, nothing is working. My executeAsyncScript executions no longer time out now that I put callback() at the end, but none of them seem to be triggering the visibilityState to actually show as visible as far as PrimeNG Toast is concerned. I don't want to have to load a plugin into Chrome if there is any way around it.
browser.driver.manage().timeouts().setScriptTimeout(15000);
const visibilityScript = `
var callback = arguments[arguments.length - 1];
window.document.addEventListener('blur', (e) => {
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
});
Object.defineProperty(window.document,'hidden',{get:function(){return false;},configurable:true});
Object.defineProperty(window.document,'visibilityState',{get:function(){return 'visible';},configurable:true});
window.document.dispatchEvent(new Event('blur'));
window.document.dispatchEvent(new Event('visibilitychange'));
window.document.dispatchEvent(new Event('focus'));
callback();
`;
await browser.wait(browser.executeAsyncScript(visibilityScript));