Spy on window function with testcafe - testing

I want to test with testcafe if a function on a window object is executed with certain parameters. Is it possible with Testcafe?
The function call looks like this:
window.myObject.myFunction({customObject: true});

You can use the ClientFunction API to create a spy function in a window object. Please look at the following test example:
import { ClientFunction } from 'testcafe';
fixture `New Fixture`
.page `https://cf51n.csb.app/`;
const spyOn = ClientFunction(() => {
// Create an array where we store information about `myFunction` calls
window.myFunctionSpyData = [];
// Store the original `myFunction` value
window.orirginalFunction = window.myObject.myFunction;
window.myObject.myFunction = function() {
// Save data about the current call
window.myFunctionSpyData.push(...arguments);
// Call the original `myFunction` value
window.orirginalFunction(...arguments);
}
});
const getSpyData = ClientFunction(() => {
// Retrieve data about myFunction calls from client
return window.myFunctionSpyData;
});
const spyOff = ClientFunction(() => {
// Restore the original myFunction value
window.myObject.myFunction = window.orirginalFunction;
delete window.spyData;
});
test('New Test', async t => {
await spyOn();
await t.click('#btn');
const data = await getSpyData();
await spyOff();
await t
.expect(data.length).eql(2)
.expect(data[0]).eql('1')
.expect(data[1]).eql('2');
});

Related

handling error trying to set key of immutable and frozen object

i have the following object in a state:
App.js
const data = {name: 'John', status:'employeed' };
const [manager, updateManager] = useState(data);
const updateStatus = async () =>{
await service.setStatus(manager);
}
setStatus does modify the manager directly, which I think is the source of the problem
service.js
setStatus(manager){
manager.status = 'employeed';
}
so I modified the call to send a copy of the manager object and return a new object but still I get the error
you tried to modify 'status' key of an immutable object.
here is my new change
App.js
const updateStatus = async () =>{
const g = await service.setStatus({...manager});
updateManager(g);
}
service.js
setStatus(manager){
manager.status = 'employeed';
return manager;
}
Why has this function to be async?
const updateStatus = async () =>{
await service.setStatus(manager);
}
This return a new/updated object but it won‘t do any permanent changes.
setStatus(manager){
return {…manager,status: 'employeed'};
}
Does the problem after your last changes persists?
You should pass a callback like this:
const updateStatus = (manager) => {
service.setStatus(()=> updateManager({
...manager,
status: 'employeed'
}));
Service.js
setStatus(updateManager){
updateManager(manager);
}

Invoking Testcafe 't' Handler Through Anonymous Self Executing Function

I have a case where I would like to use invoke the t test handler with the t.ctx context available outside of the test() function. Is this possible at all? From what I see when I run this script I do see the console spitting out all the available handler options to me, but trying to create an empty user form t.ctx.userForm = {} throws the error:
Cannot implicitly resolve the test run in the context of which the test controller action should be executed. Use test function's 't' argument instead.
import { t } from 'Testcafe';
const setFormContext = (t) => {
console.log(t);
t.ctx.userForm = {};
};
export const intializeEmptyForm = (async(t) => {
await setFormContext(t)
})(t);
I basically want to be able to have code like such, but without overcbloating the POM AccountPage object with custom functions not related to what's on the page, or relying on something like firstName to be invoked in order to make the t.ctx.userForm available.
export const AccountPage = {
enterFirstName: async (firstName) => {
let firstNameField = Selector('#firstName');
await t.typeText(firstNameField, firstName);
// t.ctx.userForm = {}; <- ideally not here as it's tied to enterFirstName
t.ctx.userForm.firstName = firstName;
},
enterLastName: async (lastName) => {
let lastNameField = Selector('#lastName');
await t.typeText(lastNameField, lastName);
t.ctx.userForm.lastName = lastName;
}
// ... some logic that maps t.ctx.userForm values to an assertion that checks all form values after clicking 'Save' are actually present.
}
import { AccountPage } from 'AccountPage';
...
test('User form successfully saves and updates correctly', async () => {
await AccountPage.enterFirstName('First');
await AccountPage.enterLastName('Last');
await AccountPage.clickSave()
})
The import {t} from 'testcafe' statement looks for a test(), beforeEach(), afterEach() or other test function in the call stack and gets the t instance from its arguments. This error occurs when an imported t is used in a function that is not called from a test or hook. This is what happens in your case, since the arrow function whose promise is exported in initializeEmptyForm is self-invoked.
As a solution, you can export a function in initializeEmptyForm (not a promise) and call it from test context.
helper.js
import { t } from 'testcafe';
export const initializeEmptyForm = async () => {
await setFormContext(t);
};
test.js
import { initializeEmptyForm } from './helper.js';
fixture 'fixture 1'
.beforeEach(async t => {
await initializeEmptyForm();
});
test('test 1', async t => {
// ...
});
Alternatively, you can export a function that takes t as an argument:
helper.js
export const initializeEmptyForm = async t => {
await setFormContext(t);
};
test.js
import { initializeEmptyForm } from './helper.js';
fixture 'fixture 1'
.beforeEach(async t => {
await initializeEmptyForm(t);
});
test('test 1', async t => {
// ...
});
My thoughts on this are when visiting a form with a click action then it may be cleaner to do so as part of the click action.
import { Selector, t } from 'testcafe';
export const AccountPage = {
clickEditForm: async () => {
let editButton = Selector('button').withText('Edit');
await t.click(editButton);
// guarantees on a click form we have setup the userForm object;
t.ctx.userForm = {};
},
enterFirstName: async (firstName) => {
let firstNameField = Selector('#firstName');
await t.typeText(firstNameField, firstName);
t.ctx.userForm.firstName = firstName;
},
enterLastName: async (lastName) => {
let lastNameField = Selector('#lastName');
await t.typeText(lastNameField, lastName);
t.ctx.userForm.lastName = lastName;
}
// map t.ctx.userForm values to assertions that checks all form values after clicking 'Save'.
verifyAccountFormDetails: async(expectFormValue = []) => {
// Grab form values
// Then map them to parameters desired or something.
}
}
This allows us to then pass the values around in a cleaner manner with the POM.
import { AccountPage } from 'AccountPage';
...
test('User form successfully saves and updates correctly', async () => {
await AccountPage.enterFirstName('First');
await AccountPage.enterLastName('Last');
await AccountPage.clickSave();
...
// After doing something like account form save then verify values
persist based on what you want to check
await AccountPage.verifyAccountFormDetails(['firstName', 'email'])
})

How to do drag only without drop in Testcafe

So I have a scenario where I want to capture a popup element message whenever I drag one element to another element.
public async dragTransitiontToSegment(item: number, transitionName: string) {
const _tailSegment = Selector('.rolling').nth(item);
const _transitionPanel = Selector('.effects-selector.editor-panel .item-container')
const _transitionType = _transitionPanel.withText(transitionName);
await t.click(_transitionPanel);
await t.dragToElement(_transitionType,_tailSegment,{speed:0.01});
}
Right now I've change the speed for the drag but it was still to fast to capture the message I want, because the dragToElement fucntion will drop it. Is there a way to just drag and hold it ?
at present, TestCafe doesn't allow you to drag without drop out of the box. You can simulate the sequence of events (the mousedown, mousemove, or HTML5 drag events)
import { Selector, ClientFunction } from 'testcafe';
function triggerMouseEvent (selector, type, options) {
const dispatchFunc = ClientFunction((type, options = {}) => {
options.bubbles = true;
options.cancelable = true;
options.view = window;
const event = new MouseEvent(type, options);
const targetElement = elementSelector();
targetElement.dispatchEvent(event);
}, { dependencies: { elementSelector: selector } });
return dispatchFunc(type, options);
}
fixture`Fixture`
.page`http://devexpress.github.io/testcafe/example`;
test('Test', async t => {
await t.click('#tried-test-cafe');
const slider = Selector('span.ui-slider-handle.ui-corner-all');
await triggerMouseEvent(slider, 'mousedown');
await t.wait(1000);
const offsetLeft = await Selector('.slider-value').withText('5').offsetLeft;
await triggerMouseEvent(slider, 'mousemove', { clientX: offsetLeft });
await t.wait(1000);
await t
.expect(slider.offsetLeft).gte(352)
.expect(slider.offsetLeft).lte(353);
});
Also, TestCafe 1.15 will include the t.dispatchEvent method that allows you to trigger events using TestController.

React Native listen to variable changes

I am trying to make an app that will allow me to update a textbox after receiving a change in a variable. However the variable takes a long time to update, and await does not work to wait for my variable to update probably because of the timeout function I used. How do I create a listener or something of that sort to check for any variable changes?
Below is my code snippet
const [banana, setBanana] = useState(-1);
const updateLocation = async() => {
const majorSig = scheduledScan();
setBanana(majorSig);
}
const scheduledScan = async() => {
beaconScan();
// Scans for 20 seconds
setTimeout( async()=> {
beaconStop();
await getAndUpdateUserLoc();
// console.log("Loggged: ", await getUserLoc());
// console.log("major: ", await getSignificantMajor());
currentMajor = await getSignificantMajor();
return currentMajor;
}, 20000);
}
When I run updateLocation(), my code is supposed to run for 20 second. I want it to wait until it finishes running scheduledScan() and returns a value to majorSig before it runs the setState function. However right now all it does is run scheduledScan() and update setState immediately to a wrong value. What should I do to make it behave in the way I want?
Thank you.
Firstly, in your async updateLocation function, your await statement is missing. Let's add it appropriately:
const updateLocation = async () => {
const majorSig = await scheduledScan();
setBanana(majorSig);
};
Then, It would be a good idea if you follow a promise approach in your time-limited function by using a Promise.race which lets your function either time out or successfully return a value:
const scheduledScan = async () => {
beaconScan();
return Promise.race([
async () => {
beaconStop();
await getAndUpdateUserLoc();
// console.log("Loggged: ", await getUserLoc());
// console.log("major: ", await getSignificantMajor());
currentMajor = await getSignificantMajor();
return currentMajor;
},
new Promise((_, reject) => setTimeout(() => reject(new Error('Request timed out')), 20000)),
]);
};

redux-thunk: actions are not dispatching

I am trying to build an app in react native that is suppose to take take two inputs by a user and then make a query to an api and get information about the two inputs. I have been having trouble with redux and redux-thunk and specifically with async actions.
This is the code in my app that i am specifically having trouble with
export const fetchData = url => {
console.log("start Fetching");
return async dispatch => { // this is where the problem is
dispatch(fetchingRequest());
try {
const response = await fetch("https://randomuser.me/api/?results=10");
const json = await response.text();
if (response.ok) {
dispatch(fetchingSuccess(json));
console.log("JSON", json);
} else {
console.log("fetch did not resolve");
}
} catch (error) {
dispatch(fetchingFailure(error));
}
};
console.log("Fetched data");
};
Upon debugging the function, I have ended with finding that when the fetchData function is called the function will execute but the async dispatch that is being returned has undefined behavior.
The output in the debugger when the function is called should be
start Fetching
JSON file information/Error
but the output in the debugger is actually
start Fetching
This is the function in which fetchData is called in
_onPress = () => {
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
this is the mapDispatchToProps function that I have added. The problem is i do not know what to add inside the function.
const mapStatetoDispatch = (url, dispatch) => {
return {dispatch(fetchData(url))}; // do not know what to place in body of function
};
i have connected it in the component with
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
these are the action creators that I import, if needed
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
Assuming you have added redux-thunk as a middleware, it looks like the errors are here:
_onPress = () => {
const { fetchData } = this.props;
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
and
const mapStatetoDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url)),
}};