Testcafe custom reporter doesn't return from this.formatError - automation

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

Related

Selenium 4.x execute "Page.addScriptToEvaluateOnNewDocument" properly

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.

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

How to wait until all async calls are finished

I've got NestJS application which interact with YoutubeAPI and load videos from it.
One particular method is important and it's loadVideos from below. Method it self has multiple asyncs inside and I need to work with videoIdMap property once everything is finished
private loadVideos(
playListId: string,
channel: Channel,
nextPageToken: string,
stopLoadingOnVideoId: string,
) {
const baseUrl = YoutubeService.VIDEO_URL_SNIPPET_BY_ID + playListId;
const response = this.httpService
.get(nextPageToken ? baseUrl + '&pageToken=' + nextPageToken : baseUrl)
.pipe(map((response) => response.data));
response.subscribe((data) => {
data.items.forEach((item) => {
if (stopLoadingOnVideoId && item.snippet.resourceId.videoId === stopLoadingOnVideoId) {
return;
}
this.prepareVideoEntity(item.snippet, channel).then((partialVideo) =>
this.videoService.create(partialVideo).then((video) => {
this.videoIdMap[video.youtubeId] = video.id;
}),
);
});
if (data.nextPageToken) {
this.loadVideos(
playListId,
channel,
data.nextPageToken,
stopLoadingOnVideoId,
);
}
});
}
Ideal solution for me would be to make loadVideos async somehow so I can later do:
public methodWhichCallLoadVideos(): void {
await loadVideos(playListId, channel, null, stopLoadingOnVideoId)
// My code which have to be executed right after videos are loaded
}
Every solution I tried out end up with this.videoIdMap to be empty object or with compilation issue so any idea is more than welcome.
You could switch to promises instead of Observables, thus turning the method into an async one that recurs as long as data has a nextPageToken:
private async loadVideos(
playListId: string,
channel: Channel,
nextPageToken: string,
stopLoadingOnVideoId: string,
) {
const baseUrl = YoutubeService.VIDEO_URL_SNIPPET_BY_ID + playListId;
const response = await this.httpService
.get(nextPageToken ? url + '&pageToken=' + nextPageToken : url).toPromise();
const { data } = response;
for (const item of data.items) {
if (stopLoadingOnVideoId && item.snippet.resourceId.videoId === stopLoadingOnVideoId) {
continue;
}
const partialVideo = await this.prepareVideoEntity(item.snippet, channel);
const video = await this.videoService.create(partialVideo)
this.videoIdMap[video.youtubeId] = video.id;
}
if (data.nextPageToken) {
await this.loadVideos(
playListId,
channel,
data.nextPageToken,
stopLoadingOnVideoId,
);
}
}
In your caller you can then simply await loadVideos(...):
private async initVideoIdMap(...) {
await this.loadVideos(...);
// this.videoIdMap should be correctly populated at this point
}

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

Karma unit test fails in phantomjs

After fixing alot of erros in the testing, now Karma output is this:
PhantomJS 1.9.8 (Windows 8 0.0.0) Controller: MainCtrl should attach a list of t hings to the scope FAILED
Error: Unexpected request: GET /api/marcas
No more request expected at ....
api/marcas is an endpoint i've created. code for MainCtrl:
'use strict';
angular.module('app')
.controller('MainCtrl', function ($scope, $http, $log, socket, $location, $rootScope) {
window.scope = $scope;
window.rootscope = $rootScope
$scope.awesomeThings = [];
$scope.things = ["1", "2", "3"];
$http.get('/api/things').success(function(awesomeThings) {
$scope.awesomeThings = awesomeThings;
socket.syncUpdates('thing', $scope.awesomeThings);
});
$scope.addThing = function() {
if($scope.newThing === '') {
return;
}
$http.post('/api/things', { name: $scope.newThing });
$scope.newThing = '';
};
$scope.deleteThing = function(thing) {
$http.delete('/api/things/' + thing._id);
};
$scope.$on('$destroy', function () {
socket.unsyncUpdates('thing');
});
$http.get('/api/marcas').success(function(marcas) {
$scope.marcas = marcas;
socket.syncUpdates('marcas', $scope.response);
$scope.marcasArr = [];
$scope.response.forEach(function(value) {
$scope.marcas.push(value.name);
});
$scope.marcaSel = function() {
for (i = 0; i < $scope.response.length; i++) {
if ($scope.selectedMarca == $scope.response[i].name) {
$scope.modelos = $scope.response[i].modelos;
};
};
};
});
until you didn't posted your test-code, I guess that your test doesn't includes the following code:
beforeEach(inject(function () {
httpBackend.expectGET('/api/marcas').respond(function(){
return {/*some status code*/, {/*some data*/}, {/*any headers*/}};
});
}));
if the karma-runner tries to execute your test-code, there are to get-requests to the $http-service, $http.get('/api/marcas') and $http.get('/api/things'). if one of these backend calls is not expected, karma cannot run the testcode successfully.
if you don't want to do special stuff for each but only return a default with success code for both calls, you can write so:
beforeEach(inject(function () {
httpBackend.expectGET(/api/i).respond(function(){
return {/*some status code*/, {/*some data*/}, {/*any headers*/}};
});
}));