Selenium 4.x execute "Page.addScriptToEvaluateOnNewDocument" properly - selenium

I'm having a strange issue where I really cannot find a solution. My production website needs some testing and of course bot checking is enabled (not naming it).
Under my tests after using "Page.addScriptToEvaluateOnNewDocument", the result is not what I would expect.
If I visit the website directly in the current tab of Chromium, it won't pass the bot checks.
If I manually open a new tab and visit the website, it passes the bot checks.
At first I thought the scripts are not being executed in the current tab, however checking things that I overwrite shows that they are.
Using this little script I've taken from puppeteer is a PASS in both tabs:
async function test() {
const results = {}
async function test(name, fn) {
const detectionPassed = await fn()
if (detectionPassed) {
console.log(`WARNING: Chrome headless detected via ${name}`)
} else {
console.log(`PASS: Chrome headless NOT detected via ${name}`)
}
results[name] = detectionPassed
}
await test('userAgent', _ => {
return /HeadlessChrome/.test(window.navigator.userAgent)
})
// Detects the --enable-automation || --headless flags
// Will return true in headful if --enable-automation is provided
await test('navigator.webdriver present', _ => {
return 'webdriver' in navigator
})
await test('window.chrome missing', _ => {
return /Chrome/.test(window.navigator.userAgent) && !window.chrome
})
await test('permissions API', async _ => {
const permissionStatus = await navigator.permissions.query({
name: 'notifications'
})
// eslint-disable-next-line
return (
Notification.permission === 'denied' && // eslint-disable-line no-undef
permissionStatus.state === 'prompt'
)
})
await test('permissions API overriden', _ => {
const permissions = window.navigator.permissions
if (permissions.query.toString() !== 'function query() { [native code] }')
return true
if (
permissions.query.toString.toString() !==
'function toString() { [native code] }'
)
return true
if (
permissions.query.toString.hasOwnProperty('[[Handler]]') && // eslint-disable-line no-prototype-builtins
permissions.query.toString.hasOwnProperty('[[Target]]') && // eslint-disable-line no-prototype-builtins
permissions.query.toString.hasOwnProperty('[[IsRevoked]]') // eslint-disable-line no-prototype-builtins
)
return true
if (permissions.hasOwnProperty('query')) return true // eslint-disable-line no-prototype-builtins
})
await test('navigator.plugins empty', _ => {
return navigator.plugins.length === 0
})
await test('navigator.languages blank', _ => {
return navigator.languages === ''
})
await test('iFrame for fresh window object', _ => {
// evaluateOnNewDocument scripts don't apply within [srcdoc] (or [sandbox]) iframes
// https://github.com/GoogleChrome/puppeteer/issues/1106#issuecomment-359313898
const iframe = document.createElement('iframe')
iframe.srcdoc = 'page intentionally left blank'
document.body.appendChild(iframe)
// Here we would need to rerun all tests with `iframe.contentWindow` as `window`
// Example:
return iframe.contentWindow.navigator.plugins.length === 0
})
// This detects that a devtools protocol agent is attached.
// So it will also pass true in headful Chrome if the devtools window is attached
await test('toString', _ => {
let gotYou = 0
const spooky = /./
spooky.toString = function() {
gotYou++
return 'spooky'
}
console.debug(spooky)
return gotYou > 1
})
return results
};
test();
My question would be, does "Page.addScriptToEvaluateOnNewDocument" really executes properly only in a new tab?
It really makes no sense to me why this is working in a new tab rather than in the current one.
Current setup and usage:
Chrome + Chrome Driver: v85.0.4183.83
Selenium Standalone: 4.1.1
Arguments:
--no-first-run --no-service-autorun --no-default-browser-check --disable-blink-features=AutomationControlled --window-size=1280,1024
excludeSwitches:
allow-pre-commit-input disable-background-networking
disable-backgrounding-occluded-windows
disable-client-side-phishing-detection disable-default-apps
disable-hang-monitor disable-popup-blocking disable-prompt-on-repost
disable-sync enable-automation enable-blink-features enable-logging
password-store test-type use-mock-keychain
Preferences:
profile.default_content_setting_values.popups 1
profile.default_content_setting_values.cookies 1
profile.cookie_controls_mode 0
UseAutomationExtension: false
Please advise, I would upmost appreciate it.

I have found out the solution. It's just how it works. Hurray.

Related

TestCafe - Can You Pass ctx (Context) Variables to reporter?

I would like to know if I have a context variable like t.ctx.data, is there a way to get that to write the value of t.ctx.data to the TestCafe JSON reporter (or any reporter)?
My code:
// Called within Express.js by a request coming from req
const testMySite = (req, res) => {
process.env.PARAMS = JSON.stringify(req.body)
let testcafe = null;
console.log(`Running test on ports 1341 and 1342`)
createTestCafe('localhost', 1341, 1342, void 0, true)
.then(tc => {
testcafe = tc;
const runner = testcafe.createRunner()
return runner
.src(`${path.dirname(__filename)}/tests/gisTest.js`)
.browsers('firefox:headless')
.reporter('json', 'report.json')
.run()
})
.then(failedCount => {
testcafe.close()
})
res.json({message: `Success! Scraper has begun to process ${req.body}`});
}
My test code:
import { ClientFunction, Selector } from 'testcafe';
const doc = process.env.PARAMS
const newDoc = JSON.parse(process.env.PARAMS)
console.log(`newDoc (from test)`, newDoc)
// const _id = newDoc._id
let data = newDoc.mydata
fixture `My Fixture`
.page('https://www.mysite.co')
.afterEach(async t => {
await t
// how do I get t.ctx.myData into the reporter??
console.log(`t.ctx.myData: `, t.ctx.myData)
})
test(`My Test`, async t => {
const photoIcon = Selector('div#sbtc div.LM8x9c > span')
const photoFieldForPaste = Selector('input#Ycyxxc')
const searchByImageButton = Selector('td#aoghAf > input')
const targetElement = Selector('div#jHnbRc span:nth-child(2) > a')
await t
.wait(1000)
.click(photoIcon)
.typeText(photoFieldForPaste, data, {paste: true})
.click(searchByImageButton)
if(await targetElement.exists && await targetElement.visible) {
await t.ctx.finalData = targetElement.innerText;
}
await t.ctx.finalData = null;
})
Please see the part // how do I get t.ctx.myData into the reporter??.
I am assuming this is the only place where I could potentially get the data from the test into the reporter but I'm not sure exactly how.
If you know how to get the t.ctx.myData variable as shown in the above code to be written to the JSON reporter, I would highly appreciate it.
Even better would be to have a way to send the t.ctx.myData value into the response.
At present, you can add only static metadata to tests and fixtures. This metadata is available in reports. Please refer to the following article to get details: https://devexpress.github.io/testcafe/documentation/guides/basic-guides/organize-tests.html#specify-test-metadata
As for sending dynamic data to the reporter, we keep this feature in mind, however we cannot give any estimates on this. Please track the following issue: https://github.com/DevExpress/testcafe/issues/3584

Testcafe: Is there a method to check for elements visible within the viewport? [duplicate]

I'm trying to implement a custom method to find out if the element is in the current view port
Below is the snippet of code that I've tried to implement but the outcome does not render the boolean result:
export const isElementInViewport = () => {
const getBoundValues = ClientFunction(() => document.querySelectorAll(".hero-getstart").item(0).getBoundingClientRect());
const windowHeight = ClientFunction(() => window.innerHeight);
const windowWidth = ClientFunction(() => window.innerWidth);
return getBoundValues.bottom > 0 && getBoundValues.right > 0 && getBoundValues.left < (windowWidth || document.documentElement.clientWidth) && getBoundValues.top < (windowHeight || document.documentElement.clientHeight);
};
The above code runs properly on the browser console, i.e when I try to store the getBoundValues in a variable A and try to run the return command, it prints the output as true or false depending on the visibility of the element in the viewport but in the script, It always gives a false:
Here's the method which triggers the above method:
export const verifyElementInView = () => {
const elementVisible = isElementInViewport();
console.log(elementVisible);
};
The output is always false.
Here's the snippet of output I receive upon trying to console.log(getBoundValues):
{ [Function: __$$clientFunction$$]
with: [Function],
[Symbol(functionBuilder)]:
ClientFunctionBuilder {
callsiteNames:
{ instantiation: 'ClientFunction',
execution: '__$$clientFunction$$' },
fn: [Function],
options: {},
compiledFnCode: '(function(){ return (function () {return document.querySelectorAll(".hero-getstart").item(0).getBoundingClientRect();});})();',
replicator:
{ transforms: [Array],
transformsMap: [Object],
serializer: [Object] } } }
What am I missing?
There's no need to create a client function for each client-side call. Instead, you can wrap the entire function into the ClientFunction call as follows:
const isElementInViewport = ClientFunction(() => {
const getBoundValues = document.querySelector("#developer-name").getBoundingClientRect();
const windowHeight = window.innerHeight;
const windowWidth = window.innerWidth;
return getBoundValues.bottom > 0 && getBoundValues.right > 0 && getBoundValues.left < (windowWidth || document.documentElement.clientWidth) && getBoundValues.top < (windowHeight || document.documentElement.clientHeight);
});
I recommend that you call your client function as follows (as described in the Executing Client Functions topic):
 
test('ClientFunctionCall', async t => {
const elementVisible = await isElementInViewport();
console.log(elementVisible)
});
 
The following example might also be useful: Complex DOM Queries

How to assert the values in a downloaded file using Cypress

I am downloading a zip file which has a json zipped. Using cy.readfile, I am able to read the content but I am not sure what commands can be used to assert on the values inside.
(Please let me know if there is a way to unzip the file before reading)
I need to verify I have 3 objectids present in the json and also some values of the elements.
I tried the below approach, but it did not work.
cy.readFile(`/Users/${username}/Downloads/${fileName}.zip`)
.should('contain','objectid').and('have.length',3);
The above command did not work for me :(
Could someone help me with some examples? I am new to cypress and coding,therefore struggling a little.
You can change the download folder in every test case!!
Look into your index.js in -> cypress -> plugins -> index.js and write this :
module.exports = (on, config) => {
on('before:browser:launch', (browser, options) => {
const downloadDirectory = 'C:\\downloads\\'; // this is the path you want to download
options.preferences.default['download'] = { default_directory: downloadDirectory };
return options;
});
};
Do it like this
cy.readFile(`/Users/${username}/Downloads/${fileName}.zip`)
.then((data) => {
// you can write whatever assertions you want on data
debugger;
console.log(data);
expect(data).to....
})
You can put debugger as above and logs to check what data contains and then assert
Use this link to know about available assertions https://docs.cypress.io/guides/references/assertions.html#BDD-Assertions
So here is the approach I am following.It is quite lengthy, but still posting as it might be helpful for someone.Please comment if you have any suggestions for improvements here.
I am using npm-unzipper to unzip the downloaded file.
Step 1: $ npm install unzipper
Step 2:In plugins > index.js
const fs = require('fs');
const os = require('os');
const osplatform = os.platform();
const unzipper = require('unzipper');
const userName = os.userInfo().username;
let downloadPath =`/${userName}/Downloads/`;
if (osplatform == 'win32'){
downloadPath = `/Users/${userName}/Downloads/`;
}
on('task', {
extractzip(zipname) {
const zipPath = downloadPath + zipname;
if (fs.existsSync(zipPath)) {
const readStream = fs.createReadStream(zipPath);
readStream.pipe(unzipper.Extract({path: `${downloadPath}`}));
const jsonname = 'testfile.json'
const jsonPath = downloadPath + jsonname;
return jsonPath;
}
else{
console.error('file not downloaded')
return null;
}
}
})
Step 3:support > commands.js
Cypress.Commands.add('comparefiles', { prevSubject: false }, (subject, options = {}) => {
cy.task('extractzip', 'exportfile.zip').then((jsonPath) => {
cy.fixture('export.json').then((comparefile) => {
cy.readFile(jsonPath).then((exportedfile) => {
var exported_objectinfo = exportedfile.objectInfo;
var compare_objectinfo = comparefile.objectInfo;
var exported_metaInfo = exportedfile.metaInfo;
var compare_metaInfo = comparefile.metaInfo;
expect(exported_objectinfo).to.contain.something.like(compare_objectinfo)
expect(exported_metaInfo).to.have.deep.members(compare_metaInfo)
})
})
});
});
Step 4: specs > exportandcompare.js
cy.get('[data-ci-button="Export"]').click();
cy.comparefiles();

Testcafe custom reporter doesn't return from this.formatError

I'm trying to implement a custom reporter for testcafe. If there is an error in testcase and this.formatError is called, it is not returning from this.formatError. Just hangs there indefinitely, I need to manually ctrl-c to stop the testcafe process. But if I remove this.formatError and try again it is working. What could be the reason?
Below is the code for reportTestDone()
reportTestDone (name, testRunInfo, meta) {
const errors = testRunInfo.errs;
const warnings = testRunInfo.warnings;
const hasErrors = !!errors.length;
const hasWarnings = !!warnings.length;
const result = hasErrors ? this.chalk.red('failed')
: this.chalk.green('passed');
const title = `${result} ${name} # ${meta.page}`;
this.write(title)
.newline();
if (hasErrors) {
this.write('Errors:')
.newline();
errors.forEach((error, idx) => {
this.setIndent(4)
.write(this.formatError(error, `${idx + 1} `)) //<-- this line is the problem
.write(error.errMsg)
.newline();
});
this.setIndent(0);
}
if (hasWarnings) {
this.newline()
.write('Warnings:');
warnings.forEach(warning => {
this.newline()
.write(warning);
});
}
this.setIndent(0);
}

How can I test two different test groups concurrently?

I'm trying concurrently run my tests except for tests that include login.
I've tried to separate tests into two groups (with login and without login). These two groups run in parallel. Tests without login are running concurrently with each other, but tests with login run one after each other. The problem is that runner only runs tests without logins twice. I saw .serial feature discussion in https://github.com/DevExpress/testcafe/issues/116 and I think it would help a lot in this situation.
const createTestCafe = require('testcafe');
const config = require('./config');
const testArrayBuilder = require('./Tests/helpers/testArrayBuilder');
let testSteps = require('./Tests/helpers/testSteps');
let testcafe;
const store = process['argv'][2],
name = process['argv'][3],
env = process['argv'][4];
const testsFile = require(`./Tests/${store}/tests.json`);
const output = 'logs/test-results-' + store + '-' + env + '-' + name + '.json';
createTestCafe('localhost', 0)
.then(tc => {
testcafe = tc;
const tests = getTests();
const runner = testcafe.createRunner();
if (name !== 'all') {
runner.filter(testName => {
return testName === name;
});
}
runner.reporter(['spec', {
name: 'json-custom',
output: output
}]);
return Promise.all(tests.map(obj => {
if(obj.login === true) {
//Don't change number value for this one,
//because login tests won't run properly.
return runTests(runner, obj.tests, 1);
} else{
return runTests(runner, obj.tests, 1);
}
}));
})
.then(() => {
testSteps.mergeWithReporter(output);
testcafe.close();
});
const runTests = (runner, tests, windows) => {
return runner
.src(tests)
.browsers(config.browsers)
.concurrency(windows)
.screenshots('logs/screenshots/', true)
.run({
skipJsErrors: true,
quarantineMode: true,
selectorTimeout: 20000,
assertionTimeout: 20000,
pageLoadTimeout: 20000,
speed: 0.5,
stopOnFirstFail: false
});
}
const getTests = () => {
const testsWithoutLogin = testArrayBuilder(testsFile[env], false);
switch (store) {
case 'shop1':
const testsWithLogin = testArrayBuilder(testsFile[env], true);
return [{login: true ,tests: testsWithLogin}, {login: false, tests: testsWithoutLogin}];
case 'shop2':
return [{login: false ,tests: testsWithoutLogin}];
}
}
Note that if I write createRunner() part in runTests(), reporter is overwritten.
I think the problem here is that you use the same runner for parallel test execution in the Promise.race function. I would recommend you use different runners for both your test sets.