Cypress: testing React app in offline mode causes test to hang - reactjs

I am testing a React application created using create-react-app. I overwrite the App.js file to display text depending on whether the browser is online/offline:
import { Offline, Online } from "react-detect-offline";
function App() {
return (
<div className="App">
<Online>
<p data-testid="online-text">online</p>
</Online>
<Offline>
<p data-testid="offline-text">offline</p>
</Offline>
</div>
);
}
export default App;
I then run npx cypress open and created the following test file:
/// <reference types="cypress" />
const goOffline = () => {
cy.log("**go offline**")
.then(() => {
Cypress.automation("remote:debugger:protocol", {
command: "Network.enable",
});
})
.then(() => {
Cypress.automation("remote:debugger:protocol", {
command: "Network.emulateNetworkConditions",
params: {
offline: true,
latency: -1,
downloadThroughput: -1,
uploadThroughput: -1,
},
});
});
};
const goOnline = () => {
cy.log("**go online**")
.then(() => {
Cypress.automation("remote:debugger:protocol", {
command: "Network.emulateNetworkConditions",
params: {
offline: false,
latency: -1,
downloadThroughput: -1,
uploadThroughput: -1,
},
});
})
.then(() => {
Cypress.automation("remote:debugger:protocol", {
command: "Network.disable",
});
});
};
describe("app", () => {
it("should render online text when online", () => {
goOnline();
cy.get("[data-testid='online-text']").should("exist");
goOnline();
});
it("should render offline text when offline", () => {
goOffline();
cy.get("[data-testid='offline-text']").should("exist");
goOnline();
});
it("should not render online text when offline", () => {
goOffline();
cy.get("[data-testid='online-text']").should("not.exist");
goOnline();
});
});
This tests the app in offline mode according to this guide. The first 2 tests run as expected but the 3rd test gets stuck in an infinite loop:

What Cypress version are you using?
I had a similar issue when after going Offline, Cypress couldn't go Online. After looking for an answer stumbled upon this reason https://github.com/cypress-io/cypress/issues/17723#issuecomment-906152925. Eventually, I was able to get my code working using Cypress version 6.9.1 and not 9.5.1

Related

Alan AI Error Uncaught Error: The Alan Button instance has already been created. There cannot be two Alan Button instances created at the same time

I am developing an E-commerce website AI powered Voice Command Using Alan AI. But Whenever I come back from another route, there's a blank page appears.and this error message shows in the console:
"Uncaught Error: The Alan Button instance has already been created. There cannot be two Alan Button instances created at the same time". What can I do?
my code is given below:
const Alan = () => {
useEffect(() => {
alanBtn({
key: alanKey,
onCommand: ({ command }) => {
if (command === 'testCommand') {
alert('This code was executed');
}
}
})
}, [])
return (
<div>
</div>
);
};
It's critical but easy...!
Use requestAnimationFrame for your webpage visual changes.
If run as a requestAnimationFrame callback, this will be run at the start of the frame.
const Alan = () => {
useLayoutEffect(() => {
function updateScreen(time) {
// Make visual updates here.
alanBtn({
key: alanKey,
onCommand: ({ command }) => {
if (command === 'testCommand') {
alert('This code was executed');
}
}
})
}
requestAnimationFrame(updateScreen);
}, [])
return (
<div>
</div>
);
};

Ionic React: Implementing InAppPurchase 2 on React Hooks

I am trying to implement InAppPurchase 2 from https://github.com/j3k0/cordova-plugin-purchase in Ionic React.
It seems that the ready event is never been called.
Here is my code:
....
import { IAPProduct, InAppPurchase2 as iap } from "#ionic-native/in-app-purchase-2";
const App: React.FC = () => {
useEffect(() => {
const init = async () => {
await initInAppPurchase();
setInitialized(true);
}
init();
}, [])
....
}
export const initInAppPurchase = () => {
if (isPlatform('android') || isPlatform('ios')) {
iap.verbosity = iap.DEBUG;
iap.register({
id: "com.mysoftwares.posapp.test",
alias: "Test",
type: iap.NON_CONSUMABLE
});
iap.when("com.mysoftwares.posapp.test").updated((product: IAPProduct) => {
if (product.owned)
console.log('Product owned')
else
console.log('Product not owned')
});
iap.when("com.mysoftwares.posapp.test").approved(function (product: any) {
product.finish();
});
iap.ready(() => {
alert("Product ready!")
let product = iap.get('Test');
alert(product);
if (product.canPurchase) {
iap.order('Test');
}
})
iap.refresh();
}
}
Here is the log from debug verbose:
[store.js] DEBUG: state: com.mysoftwares.posapp.test -> registered
[store.js] DEBUG: store.trigger -> triggering action refreshed
InAppBilling[js]: setup ok
InAppBilling[js]: load ["com.mysoftwares.posapp.test"]
InAppBilling[js]: listener: {"type":"ready","data":{}}
[store.js] DEBUG: store.trigger -> triggering action refresh-finished
InAppBilling[js]: setup ok
InAppBilling[js]: load ["com.mysoftwares.posapp.test","com.mysoftwares.posapp.test"]
InAppBilling[js]: listener: {"type":"ready","data":{}}
InAppBilling[js]: setup ok
InAppBilling[js]: load ["com.mysoftwares.posapp.test","com.mysoftwares.posapp.test","com.mysoftwares.posapp.test"]
InAppBilling[js]: listener: {"type":"ready","data":{}}
InAppBilling[js]: setup ok
I am using React Hooks. I have published my app in Google Play Console and have added the test product in In-app Products section inside Google Play Console. Currently my app is on open testing phase.
I have created an app before coded in pure Apache Cordova without Ionic, it works fine, but this in React is not.
What is wrong with my code? Please help...
I am testing it on emulator which is dumb. On mobile devices, it works fine.
I had to do the following:
Install these in terminal
npm install #ionic-native/in-app-purchase-2
npm install cordova-plugin-purchase
This code in .tsx file
import React, { useState, useEffect } from 'react';
import { InAppPurchase2 as iap, IAPProduct } from "#ionic-native/in-app-purchase-2";
const Home: React.FC = () => {
//declare variables
const [productPrice, setPrice] = useState('')
const [product, setProduct] = useState([]) as any
//initiate initInAppPurchase function
useEffect(() => {
const init = async () => {
await initInAppPurchase();
}
init();
}, []);
//if on an ios or android device, then get product info
const initInAppPurchase = () => {
if ((isPlatform('ios')) || (isPlatform('android'))) {
iap.verbosity = iap.DEBUG;
iap.register({
id: "com.test.test",
alias: "Test",
type: iap.NON_CONSUMABLE
});
iap.ready(() => {
let product = iap.get('Test');
setPrice(product.price)
setProduct(product)
})
iap.refresh();
}
}
//if user clicks purchase button
const purchaseProduct = () => {
if (product.owned) {
alert('Product already owned, click restore button instead!')
} else {
iap.order('Test').then(() => {
iap.when("com.test.test").approved((p: IAPProduct) => {
//store product
p.verify();
p.finish();
});
})
iap.refresh();
}
}
//if user clicks retore or promo code button
const restore = () => {
iap.when("com.test.test").owned((p: IAPProduct) => {
if (product.owned) {
//store product
} else {
alert("You have not purchased this product before.")
}
});
iap.refresh();
}
return (
<IonPage>
<IonContent fullscreen>
<IonCard className="homeCards">
<IonButton onClick={purchaseProduct}>Buy for {productPrice}</IonButton>
<IonButton onClick={restore}>Restore</IonButton>
<IonButton onClick={restore}>Promo code</IonButton>
</IonCard>
</IonContent>
</IonPage>
);
};
export default Home;
To test apple in app purchase on device:
Had a developer account
Created in app purchase in apple connect
Added my email as a tester in apple connect
Uploaded app onto phone from Xcode and purchases worked
To test android in app purchase on device:
<uses-permission android:name="com.android.vending.BILLING" />
Added above line of code to AdnroidManifest.xml file
Had a developer account
Uploaded the .aab or .apk file to internal testing on google play console and added my email as a tester
Created in app purchase
Uploaded app onto phone from Android Studio and purchases worked

How to reset recaptcha when using react-redux-firebase

I am working with React-Redux-Firebase. I implemented signing in with phone number. Now I am trying to implement error handling. When number is invalid I display window alert with error message. The only thing left to do is to reset recaptcha. Without it, I am getting error:
reCAPTCHA has already been rendered in this element
I was trying to do according to Firebase documentation
grecaptcha.reset(window.recaptchaWidgetId);
// Or, if you haven't stored the widget ID:
window.recaptchaVerifier.render().then(function(widgetId) {
grecaptcha.reset(widgetId);
}
but it does not work in my code. I dont have grecaptcha implemented. I tried to add it with react-grecaptcha, but it did not work.
Could someone give me a hint how to reset recaptcha after each error, please?
state = {
phone: "",
confirmationResult: {},
};
handleClick = () => {
const recaptchaVerifier = new firebase.auth.RecaptchaVerifier(
"sign-in-button",
{
size: "invisible",
}
);
firebase
.signInWithPhoneNumber(`+${this.state.phone}`, recaptchaVerifier)
.then((confirmationResult) => {
this.setState({ confirmationResult });
})
.catch((error) => {
// Error; SMS not sent
// Handle Errors Here
window.alert(`${error.code}, ${error.message}`);
recaptchaVerifier.reset(); // How can I do that?
});
};
I've been struggling with this problem for several days, maybe my answer will help someone.
export const requestRecaptchVerifier = () => {
window.recaptchaVerifier = new RecaptchaVerifier(
"recapcha-container",
{
size: "invisible",
},
auth
);
};
I then call signInWithPhone from another function and handle the error like this:
await signInWithPhone(formik.values.phone)
.then(() => {
// ... my code
})
.catch(() => {
window.recaptchaVerifier.recaptcha.reset();
window.recaptchaVerifier.clear();
});
All the difference in
window.recaptchaVerifier.recaptcha.reset()
And
window.recaptchaVerifier.clear()
I'm no expert but from the documentation and by talking with you in the comment section I think you need to pass a callback. Like this:
const recaptchaVerifier = new firebase.auth.RecaptchaVerifier('sign-in-button', {
'size': 'invisible',
'callback': function(response) {
// reCAPTCHA solved, allow signInWithPhoneNumber.
firebase
.signInWithPhoneNumber(`+${this.state.phone}`, recaptchaVerifier)
.then((confirmationResult) => {
this.setState({ confirmationResult });
})
.catch((error) => {
// Error; SMS not sent
// Handle Errors Here
window.alert(`${error.code}, ${error.message}`);
recaptchaVerifier.reset();
});
}
});
Reference: https://firebase.google.com/docs/auth/web/phone-auth#use-invisible-recaptcha
Hope this helps!

I am trying to preserve the Session cookie through multiple tests in cypress but Cypress.Cookie.preserveOnce() is not working

Am I implementing this functionality wrong or am i missing something?? I watch the application tab of the developer tools, the cookie appears after the setToken() method but despite the Cypress.Cookies.preserveOnce('token_name') in the before each hook. The token is still erased in between tests.
before(() => {
cy.setToken()
})
beforeEach(() => {
Cypress.Cookies.preserveOnce("token_name")
cy.visit(urlPrefix + 'applications').wait(beforeEachWait)
})
after(() => {
cy.clearCookies()
})
it('Form should have title', () => {
cy.contains('Edit Application').should('be.visible')
})
it('The Save button should be disabled until changes have been made', () => {
cy.get('[data-cy=saveBtn]').should('be.disabled')
cy.get('[data-cy=applicationName] input').type(' edited')
cy.get('[data-cy=saveBtn]').should('be.enabled')
})
it('The cancel button redirects to the list view', () => {
cy.get('[data-cy=cancelBtn]')
.click()
.wait(500)
cy.url().then(url => {
const applicationsTitle = url.split('#/')[1]
expect(applicationsTitle).to.equal('applications')
})
})
it('should have delete button in edit mode', () => {
cy.get('[data-cy=deleteBtn]').should('be.visible')
})
})
use this one
Cypress.Cookies.defaults({
preserve: 'session_id',
})
it will work or
before("cookies",() => {
cy.setCookie('session_name', 'value')
}
cypress latest version 10.8.0 no need to add setCookie. Add expermentalSessionAndOrigin and chromeWebSecurity in cypress.config.ts file
e2e: {
baseUrl: "xxxxxxxxxxxxxxxxxxx",
experimentalSessionAndOrigin: true,
chromeWebSecurity: false,
}

service-worker.js not receiving message

Im trying to get my service-worker.js to execute skipWaiting when a new service worker gets uploaded. However, im not successful in performing this. Please note that my main framework is reactjs
Tried following this guide (https://deanhume.com/displaying-a-new-version-available-progressive-web-app/) and adapting the code to mine but wasnt successful either.
Currently Im only able to receive notifications that the a new service worker is available, this was observed and confirmed under the browser-dev-tools as well.
Here's my code:
index.js
import "#babel/polyfill";
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
function check() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(reg => {
reg.addEventListener('updatefound', () => {
// A wild service worker has appeared in reg.installing!
const newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
// Has network.state changed?
switch (newWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// new update available
window.serviceWorkerCallback('new_version')
newWorker.postMessage({ action: 'skipWaiting' });
console.log("New Version!")
}
// No update available
break;
}
});
});
});
};
}
check();
window.serviceWorkerCallback('new_version') from index.js activates _serviceWorkerCallback function under App.js which triggers my reload command. After refreshing my page, I could see that the new service worker is still waiting. Below is my reload command code:
App.js:
_serviceWorkerCallback = (state) => {
if (state === 'new_version') {
this.setState({
hasNewVersion: true
});
}
}
_renderNewVersion = () => {
return (
<Snackbar
style={{ marginBottom: 16 }}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
open={this.state.hasNewVersion}
message={<span>A new version is available</span>}
action={<Button color="secondary" size="small" onClick={this._handleRefresh}>Refresh</Button>}
/>
);
}
_handleRefresh = (value) => {
this.setState({
hasNewVersion: false,
});
window.location.reload();
}
Here is service-worker.js :
self.addEventListener('message', event => {
console.log('Skipping');
if (event.data.action === 'skipWaiting') {
self.skipWaiting();
}
});
self.addEventListener('fetch', function (event) {
console.log('Fetching');
event.respondWith(
caches.match(event.request)
.then(function (response) {
if (response) {
return response;
}
return fetch(event.request);
})
);
});
_handleRefresh = (value) => {
this.setState({
hasNewVersion: false,
});
window.location.reload();
}
Im expecting newWorker.postMessage({ action: 'skipWaiting' }); to trigger event.data.action === 'skipWaiting' but nothing happened and no log messages has been observed from the console.
Both files are residing under the same directory.
In the tutorial you linked, the code includes a call to reload the page but I do not see that in yours.
The reload is important because it allows the newly activated service worker to become the active worker once the page calls register. Alternatively you can use Clients.claim() to let your new service worker take immediate control of existing pages.

Resources