Click function in Spectron doesn't click - javascript

I'm working on a electron(-nuxt) based application. End to End test re-written with AVA + Spectron. The .click() function however doesnt seem to work.
I used this template:
https://github.com/michalzaq12/electron-nuxt
Everything seems to work except a simple button click.
<footer class="modal-card-foot">
<button id="loginButton" class="button " type="button" #click="login">
Login
</button>
</footer>
test('app should login', async t => {
let app = t.context.app
await app.client.click('#loginButton')
})
The message i got is:
1 test failed
app should login
Error: Test finished without running any assertions
That is truthy because there aren't any assertions.
BUT i can see that the Button is never clicked, because that would trigger a "Login failed" message from the app.

In your test case you should wait for element to be rendered on the page.
test('app should login', async t => {
const ELEMENT_SELECTOR = '#loginButton'
let app = t.context.app
try {
await app.client.nuxt.ready()
await app.client.nuxt.navigate('/') //optional
await app.client.waitForExist(ELEMENT_SELECTOR)
await app.client.click(ELEMENT_SELECTOR)
t.pass() //or other assertion
} catch (e) {
t.fail(e.message)
}
})

Checkout this repo that is an example of how to test an electron app:
https://github.com/StephenDavidson/electron-spectron-example
specifically this where they test the functionality of pressing a button.
Notice how they import in the page at the top.
Search.js
const SearchPage = require('./page-objects/search.page');
Then near the bottom they test the functionality of click
it('should search', async() => {
const input = 'this is a test';
await app.client.url(config.url)
.setValue(SearchPage.searchField, input)
.getValue(SearchPage.searchField)
.should.eventually.equal(input)
.click(SearchPage.searchButton)
.element(SearchPage.searchResult)
.should.eventually.exist;
});
See if this helps you get further along.

Related

Expo App keeps crashing on Android while trying to navigate

I have an expo managed app with a button on the screen, when the button is pressed several API calls are made to my backend and finally after the last API call depending on whether the Platform OS is ios or android i would like the app to navigate to a specific screen.
On IOS, everything is working accordingly.
On Android, the expo app crashes and closes before navigating to the next screen.
Here is my onButtonClick function:
const handlePayment=async()=>{
setLoading(true);
const sessionResponse = await paymentApi.paymentSession(
selected,route.params.transaction.id,cvv
);
if(!sessionResponse.ok){
setLoading(false)
if(sessionResponse.data){
Alert.alert(sessionResponse.data.error)
}else{
Alert.alert("Unexpected Error Occurred while creating session.")
}
}
console.log('create and update session',sessionResponse)
if(sessionResponse.data.session.updateStatus==='SUCCESS'){
const initiateResponse = await paymentApi.initiateAuthentication(
sessionResponse.data.session.id,route.params.transaction.id
);
if(!initiateResponse.ok){
setLoading(false)
if(initiateResponse.data){
Alert.alert(initiateResponse.data.error)
}else{
Alert.alert("Unexpected Error Occurred while initiating authentication")
}
}
console.log('Initiate Authentication',initiateResponse.data);
if(initiateResponse.data.result==='SUCCESS'){
setInitiateWebView(true);
const redirectHTML=(initiateResponse.data.authentication.redirectHtml).replace(/\\/g, '');
setInitiateWebViewUrl(redirectHTML);
console.log('session id ', sessionResponse.data.session.id)
const authenticateResponse = await paymentApi.authenticatePayer(
sessionResponse.data.session.id,route.params.transaction.id
);
if(!authenticateResponse.ok){
setLoading(false)
if(Platform.OS !== 'ios'){
clearInterval(myInterval)
}
Alert.alert("Unexpected Error Occurred","Please try again")
}
console.log('authenticate payer',authenticateResponse.data)
const authenticationResponseData = authenticateResponse.data;
console.log('authenticate payer authenticate data',authenticationResponseData.authentication);
const payerInteraction = authenticationResponseData.authentication.payerInteraction;
if(authenticationResponseData.authentication['3ds2']){
const status = authenticationResponseData.authentication['3ds2'].transactionStatus;
if(payerInteraction==='REQUIRED'&&status==="C"){
console.log("Challenge Flow")
setAuthenticateWebViewUrl(authenticationResponseData.authentication.redirectHtml.replace(/\\/g, ''))
console.log('Auth webview url::::::',authenticationResponseData.authentication.redirectHtml.replace(/\\/g, ''))
----> this is the last thing that appears on console before android app crashes
if(Platform.OS==='ios'){
setLoading(false);
navigation.navigate(routes.PAYWITHTOKEN,{html:authenticationResponseData.authentication.redirectHtml.replace(/\\/g, '')})
}else {
setLoading(false);
navigation.navigate(routes.PAYWITHTOKENANDROID)
}
}
The other screen is just a blank screen. I have tried navigating to that screen somewhere else inside my app and its working. The problem is here.
Follow instructions for watch logcat:
Open android studio

Run a cypress command if content is present and skip if content is not present

Hi I am working with cypress for UI testing. Based on user we do have additional button on the page.
Is there a way cypress can look for a button and run the commands if button is present and skip the command if button is absent thus preventing from element not found error.
<div class="btn btn-success" id="editButton">Edit</div>
Cypress code is
if (!cy.get('div[id=editButton]')) {
this.skip();
} else {
cy.get('div[id=editButton]').click();
}
And yet cypress throws element not found error.
Perhaps use a variation of Dynamic Text test
cy.get('body').then(($body) => {
const button = $body.find('div[id=editButton]')
if (button.length) {
// yup found it
cy.get('div[id=editButton]').click()
}
// don't need this.skip()
})
Please see the caveats on that page about conditional testing. If your $body.find('div[id=editButton]') fails because the button has not appeared yet, you will need to add more assertions to the test.
Another approach is to test for different classes of user. This makes your test suite more complete
For example,
it('tests the admin user', () => {
cy.login('admin')
cy.get('div[id=editButton]').click()
...
callCommonUserTests()
})
it('tests the read-only user', () => {
cy.login('read-only')
// cy.get('div[id=editButton]').click() // edit button not available
...
callCommonUserTests()
})
Now the test suite is much simpler and less prone to timing issues.
You can consolidate code common for all users in functions or custom commands.
To use jQuery with Cypress.$ you can test the existence without failing the test
it('tests the edit button', () => {
if (Cypress.$('#editButton').length) {
cy.get('div[id=editButton]').click()
// rest of test
}
})
Or
it('tests the edit button', () => {
if (!Cypress.$('#editButton').length) {
this.skip() // exit the test here
}
cy.get('div[id=editButton]').click()
// rest of test
})
I would go with the below option.
cy.get('div[id="editButton"]')
.then (($element) => {
if($element.length) cy.get('div[id=editButton]').click()
})
Instead of using the cypress command you have to use a JQuery command for this. And in that you have to check the length. If its 0, then the element doesn't exist.
if (Cypress.$('div[id=editButton]').length == 0) {
this.skip()
} else {
cy.get('div[id=editButton]').click()
}
You can check if the Edit button is on the page without failing the test
cy.get('div.btn').then($buttons => {
if ($buttons.text().includes('Edit')) {
cy.get('div[id=editButton]').click()
})
})

Async/Await a Page Reload in React TypeScript

I am having a button, and when I click on it, it should do the following things in order:
First refresh the Page
Then Open the Side Pane in React
This is my code snippet that is existing:
<button onClick={() => {
refreshPage();
setPaneIsOpen(true);
}}>Click me</button>
const refreshPage = () => {
window.location.reload();
}
setPaneIsOpen is a State that when set to true, will open the side pane. I want to specifially first refresh the page and then set the Pane.
I have tried this async/await promise approach:
function refreshPagePromise() {
return new Promise<void>((resolve) => {
window.location.reload();
resolve();
});
}
async function refreshAndOpen() {
await refreshPagePromise();
setIsPaneOpen(true);
}
<button onClick={() => { refreshAndOpen(); }}>Click me</button>
But I cannot seem to handle page reload. I tried to create a Code Sandbox, but since this is very complex, I could not. I will try to add one in the edits If I was able to reproduce.
One hacky way would be to use localStorage.
add this useEffect that runs on page load:
useEffect(() => {
const shouldOpenPane = !!localStorage.getItem("setPaneOpen")
if(shouldOpenPane){
setIsPaneOpen(true);
// Maybe the row beneath should not be there,
// Idk what behaviour u want if user reloads page manually
localStorage.removeItem("setPaneOpen")
}
}, []);
Update your button like this:
<button
onClick={() => {
localStorage.setItem("setPaneOpen", "true"); // can be set to any thruthy value
refreshPage();
}}
>
Click me
</button>

Handling a Windows confirm pop-up using Cypress

I am learning Cypress the hard way: on a legacy app with frames :(
I read that Cypress auto accepts alerts but here I have a Confirm pop-up that demands a user input. However, I am struggling to close this windows confirm pop ip asking to 'Cancel' or 'OK'.
The element which fires the pop-up is within a frame (not an iFrame) is as follows:
<a href="/tasksgui/manageScheduledJobs.do?userAction=runnow&userAction=select&selectedIndex=1&formContextKey=ListChunk[SchedulerJobsSearchResults]{1588676256461}1"
onclick="return(confirmRunNow())" ;>
RunJobs</a>
I know that the Cypress API exposes a way to handle these:
cy.on('window:confirm', (str) => {
//code here
}
But I'm unclear how to incorporate this into my test block"
it('gets the post', (done) => {
cy.visit('http://myapp.co.uk/mygui/index.jsp');
getLeftFrameBody().findByText('Manage Tasks').click();
cy.wait(2000)
getContentFrameBody().should('include.text', 'Scheduled Tasks')
getContentFrameBody().findByText('Task Name');
getContentFrameBody().find('input[name="jobName"]').type('Task one');
getContentFrameBody().findByText('Search').click();
cy.wait(2000)
cy.on('window:confirm', function(confirmText){
return true
});
getContentFrameBody().find('.resultrowone').find('a').eq(5).click();
})
By making the function async, you can await the window confirmation and then continue as seen in the example below.
it('gets the post', async (done) => {
cy.visit('http://companyapp.co.uk/mygui/index.jsp');
getLeftFrameBody().findByText('Manage Tasks').click();
cy.wait(2000)
getContentFrameBody().should('include.text', 'Scheduled Tasks')
getContentFrameBody().findByText('Job Name');
getContentFrameBody().find('input[name="jobName"]').type('runTasks');
getContentFrameBody().findByText('Search').click();
cy.wait(2000);
await new Promise(resolve => {
cy.on('window:confirm', (str) => {
resolve();
});
getContentFrameBody().find('.resultrowone').find('a').eq(5).click();
});
// Continue after window was confirmed
});

TestCafe persist data when clicking

In TestCafe, on click of an anchor tag, I have a redirect to a new page and an event that basically logs the click event to an object.
I'm wanting to test the value of the click event in the object in TestCafe, but because the redirect happens, I lose the object.
Manually I'm able to do this, as I can hold shift while clicking the link and open a new window for the redirected page, but keep the original page in the original window and then see the object in the console.
Trying to figure out if there's a way "simulate" a click, but not do the redirect. Or alternatively, be able to assert somehow immediately after the click, but before the redirect?
mini example:
await t
.click('a') // here it already redirects so I'll lose the object
.expect(object).contains('value')
The following test shows how you can disable and enable navigation for a link:
import { Selector, ClientFunction } from 'testcafe';
fixture `Navigation`
.page `example.com`;
const enableNavigationControl = ClientFunction(selector => {
const element = selector();
element.addEventListener('click', event => window.disableNavigation && event.preventDefault());
});
const disableNavigation = ClientFunction(() => window.disableNavigation = true);
const enableNavigation = ClientFunction(() => window.disableNavigation = false);
test('navigation', async t => {
const link = Selector('a');
await enableNavigationControl(link);
await disableNavigation();
await t.click(link);
// Perform assertions...
await enableNavigation();
await t.click(link);
});

Categories