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

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

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.

$nextTick running before previous line finished

I have a vue function call which is triggered when selecting a radio button but it seems that my code inside my $nextTick is running before my previous line of code is finished. I don't want to use setTimout as I don't know how fast the user connection speed is.
findOrderer() {
axios.post('/MY/ENDPOINT')
.then((response) => {
this.orderers = response.data.accounts;
console.log('FIND_ORDER', this.orderers)
...OTHER_CODE
}
rbSelected(value) {
this.findOrderer();
this.newOrderList = [];
this.$nextTick(() => {
for (var i = 0, length = this.orderers.length; i < length; i++) {
console.log('FOR')
if (value.srcElement.value === this.orderers[i].accountType) {
console.log('IF')
this.newOrderList.push(this.orderers[i]);
}
}
this.$nextTick(() => {
this.orderers = [];
this.orderers = this.newOrderList;
console.log('orderers',this.orderers)
})
})
}
Looking at the console log the 'FINE_ORDERER' console.log is inside the 'findOrderer' function call so I would have expected this to be on top or am I miss using the $nextTick
That's expected, since findOrderer() contains asynchronous code. An easy way is to simply return the promise from the method, and then await it instead of waiting for next tick:
findOrderer() {
return axios.post('/MY/ENDPOINT')
.then((response) => {
this.orderers = response.data.accounts;
console.log('FIND_ORDER', this.orderers);
});
},
rbSelected: async function(value) {
// Wait for async operation to complete first!
await this.findOrderer();
this.newOrderList = [];
for (var i = 0, length = this.orderers.length; i < length; i++) {
console.log('FOR')
if (value.srcElement.value === this.orderers[i].accountType) {
console.log('IF')
this.newOrderList.push(this.orderers[i]);
}
}
this.orderers = [];
this.orderers = this.newOrderList;
console.log('orderers',this.orderers)
}

App crash only if im not using debugger. Unable to overcome the situation. What should I do?

I'm facing a pretty ugly issue that I'm not able to overcome by myself, I just don't get it.
A short summary of my app purpose: You can find food around and make orders to restaurant owners.
I wrote a helper function that allows me to decide if the restaurant is open or closed based on its schedule “horarioSemanal” property.
This function take the restaurants queried from firestore as arguments and depending in some conditions it decide which value of the property “disponible” (available) it deserves.
The thing is that it works pretty well! So well that I published the changes through expo, and as expo has updates over-the-air and I have my app in both apptore and Google play it reached all my users…
None of my users were able to use my app until I removed the changes because I was unable to detect the issue. In my simulator it worked 100% but when the app was deployed crashed almost instantly.
Testing, testing, and testing I finally come to the issue but I still can't figure out what the hell should I do to overcome this situation.
The app while I use the js debugger it works perfectly! but when I turned off this new module I wrote doesn't work.
I recorded a video so you can see the same I'm watching on my screen:
https://www.youtube.com/watch?v=x9-t8-3XzKc
this is the action where im dispatching the action:
import { restaurantesHorarioValidado, validaDisponibilidadComidas } from '../../src/3-ResultadosComponents/Librerias/DisponibilidadHorario';
export const searchResultsInLocation = (ubicacion) => {
const db = firebase.firestore();
const docRef = db.collection('Restaurantes').where('abierto', '==', true).where(`zonaOperativa.zonaConsulta.${ubicacion}`, '==', true).get();
const restaurantesIDS = [];
return (dispatch) => {
const holder = [];
dispatch({
type: BUSQUEDA_DE_RESULTADOS,
});
docRef.then(querySnapshot => {
querySnapshot.forEach(doc => {
holder.push(doc.data());
restaurantesIDS.push(doc.data().id);
});
dispatch({
type: DESCARGA_RESTAURANTES_ABIERTOS,
restaurantes: restaurantesHorarioValidado(holder)
});
})
.then(() => {
const comidasRefs = [];
restaurantesIDS.forEach(restaurant => {
const ref = db.collection('Comidas').where('restaurantID', '==', `${restaurant}`).get();
comidasRefs.push(ref);
});
return Promise.all(comidasRefs).then(results => {
const comidas = [];
results.forEach(resto => {
resto.forEach(comida => comidas.push(comida.data()));
});
dispatch({
type: DESCARGA_COMIDAS,
comidas: validaDisponibilidadComidas(comidas, restaurantesHorarioValidado(holder))
});
})
.then(() => dispatch({
type: BUSQUEDA_DE_RESULTADOS,
}))
.catch(err => console.log('error; ', err));
});
};
};
here is how the reducer is handling the action:
case DESCARGA_COMIDAS:
return { ...state, comidas: action.comidas };
case DESCARGA_RESTAURANTES_ABIERTOS:
return { ...state, restaurantes: action.restaurantes };
this is the module I wrote and I'm using to create the object that the action creator send:
const diasDeSemana = ['Domingo', 'Lunes', 'Martes', 'Miercoles', 'Jueves', 'Viernes', 'Sabado'];
const today = new Date().getDay();
const hoyAbre = (horario) => {
if (horario) {
const JornadaHoy = horario.find(jornada => jornada.dia == diasDeSemana[today]);
return JornadaHoy;
}
return false;
};
export const isRestaurantAvalaible = (horario) => {
const Today = new Date();
if (hoyAbre(horario)) {
//Si el restaurant abre hoy
//Evalua si está abierto
const horarioApertura = () => {
const nuevoDia = new Date(`${Today.getFullYear()}, ${Today.getMonth() + 1} ${Today.getDate()} ${hoyAbre(horario).horario.apertura}:00`);
return nuevoDia;
};
const horarioCierre = () => {
//Si horario de cierre es hoy
const nuevoDia = new Date(`${Today.getFullYear()}, ${Today.getMonth() + 1} ${Today.getDate()} ${hoyAbre(horario).horario.cierre}:00`);
//Si el horario de cierre de hoy es pasado las 00:00
const cierraTomorrow = new Date(`${Today.getFullYear()}, ${Today.getMonth() + 1} ${Today.getDate() + 1} ${hoyAbre(horario).horario.cierre}:00`);
if (nuevoDia.getHours() <= 8) {
return cierraTomorrow;
}
return nuevoDia;
};
const isNowOpen = Today.getTime() >= horarioApertura().getTime();
const isNowClosed = Today.getTime() >= horarioCierre().getTime();
//Si está abierto
if (isNowOpen && !isNowClosed) {
return { estado: 'abierto' };
}
//Si abre mas rato
if (hoyAbre(horario) && (Today.getTime() < horarioApertura())) {
return { estado: 'abre pronto', horarioApertura: horarioApertura() };
}
//Si ya cerró
if (isNowOpen && isNowClosed) {
return { estado: 'ya cerro', horarioCierre: horarioCierre() };
}
}
//Si hoy no abre
if (!hoyAbre(horario)) {
return { estado: 'No abre hoy' };
}
};
export const restaurantesHorarioValidado = (restaurantes) => {
const restaurantesModificados = restaurantes.map(restaurant => {
return { ...restaurant, disponible: isRestaurantAvalaible(restaurant.horarioSemanal) };
});
const restaurantesAbiertos = restaurantesModificados.filter(restaurant => restaurant.disponible.estado == 'abierto');
const restaurantesProximosAbrir = restaurantesModificados.filter(restaurant => restaurant.disponible.estado == 'abre pronto');
const restaurantesCerrados = restaurantesModificados.filter(restaurant => restaurant.disponible.estado == ('ya cerro' || 'No abre hoy'));
return [...restaurantesAbiertos, ...restaurantesProximosAbrir, ...restaurantesCerrados];
};
export const validaDisponibilidadComidas = (comidas, restaurantes) => {
//Se le agrega la propiedad "disponible" del restaurant dueño
const comidasModificadas = comidas.map(comida => {
const Owner = restaurantes.find(restaurant => restaurant.id == comida.restaurantID);
return { ...comida, disponible: Owner.disponible };
});
const comidasDisponibles = comidasModificadas.filter(comida => comida.disponible.estado == 'abierto');
const comidasProximosAbrir = comidasModificadas.filter(comida => comida.disponible.estado == 'abre pronto');
const comidasNoDisponibles = comidasModificadas.filter(comida => comida.disponible.estado == ('ya cerro' || 'No abre hoy'));
return [...comidasDisponibles, ...comidasProximosAbrir, ...comidasNoDisponibles];
};
this is the error I get once I turned off the js debugger:
[Unhandled promise rejection: TypeError: undefined is not an object (evaluating 'restaurant.disponible.estado')]
Stack trace:
src/3-ResultadosComponents/Librerias/DisponibilidadHorario.js:65:98 in <unknown>
src/3-ResultadosComponents/Librerias/DisponibilidadHorario.js:65:62 in restaurantesHorarioValidado
store/actions/2-ResultadosActions.js:55:50 in <unknown>
node_modules/promise/setimmediate/core.js:37:14 in tryCallOne
node_modules/promise/setimmediate/core.js:123:25 in <unknown>
...
As it suggest there is some error with promises I tried making those functions work as promises. The errors disappear but the I didn't get the object back...
The question is this How the hell may this work just when the debugger it's on and not when it's turned off?
what should I do to get my life back?

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

How to yield in map?

I am using react native to build my application and I am trying to convert my variables to camelCase using the code below
export default function formatToCamelCase(inputObject: {[key: string]: any}) {
let snakeKeys = Object.keys(inputObject);
let newObject = {};
for (let key of snakeKeys) {
if (typeof inputObject[key] !== 'object') {
newObject[parseToCamelCase(key)] = inputObject[key];
} else if (inputObject[key] !== null && !Array.isArray(inputObject[key])) {
newObject[parseToCamelCase(key)] = formatToCamelCase(inputObject[key]);
} else {
newObject[parseToCamelCase(key)] = inputObject[key];
}
}
return newObject;
}
export function formatArrayToCamelCase(inputArray: Array<{[key: string]: any}>) {
return inputArray.map((object) => {
return formatToCamelCase(object);
});
}
And I am trying to call the formatArrayToCamelCase method in the function here (in a separate file):
import formatToCamelCase, {formatArrayToCamelCase} from '../helpers/formatToCamelCase';
export function* fetchWeighInWeighOutEvent(action: FetchWeighInWeighOutEventAction): any {
...
Object.values(locations).map((location) => {
let timeslotsArray = location.timeslots;
if (timeslotsArray.length > 0) {
//Problem here - shows that yield is a reserved word
let camelCaseTimeslots = yield call(formatArrayToCamelCase, timeslotsArray);
}
});
I was unable to put the yield call in the .map, when trying to run the code, I get the following error:
SyntaxError: yield is a reserve word
How can I overcome this issue so that I can call the formatArrayToCamelCase function and convert my array accordingly?