Why does not cypress alias work on Github CI? - reactjs

I have a problem in github ci, it cannot find the alias, and think it even doesn't define that, but all is well on local. I tested on both cypress:open and cypress:run
this is the command I defiend:
Cypress.Commands.add("byPassLogin", () => {
const url = Cypress.env("api_url");
const token = "...";
cy.saveToLocalStorage("auth_token", token);
cy.intercept("POST", url, (req) => {
if (req.body.operationName === "me") {
req.reply({
statusCode: 200,
body: {
data: {
me: { id: "1", email: "test#email.com" },
},
},
});
}
}).as("byPassLogin");
});
and then I used it on beforeEach like this
describe("test account functionality", () => {
const URL = Cypress.env("api_url");
beforeEach(() => {
cy.visit("/");
cy.byPassLogin();
});
it.only("should logout when click on nav bar", () => {
cy.intercept("POST", URL, (req) => {
if (req.body.operationName === "signOut") {
req.reply({
statusCode: 200,
body: {
data: { updateUser: { errors: null, user: { id: "1" } } },
},
});
}
}).as("signOut");
cy.wait("#byPassLogin").then(() => {
cy.url().should("include", "/app");
cy.get("#account").click();
cy.get("#logout").click();
cy.wait("#signOut").then(() => {
cy.url().should("include", "/login");
});
});
});
});
I used another approach, it works on local but still not work on CI
Cypress.Commands.add("byPassLogin", () => {
const url = Cypress.env("api_url");
const token = "...";
cy.intercept("POST", url, (req) => {
req.reply({
statusCode: 200,
body: {
data: {
login: { user: { id: "1", email: "test#email.com" }, token },
},
},
});
}).as("byPassLogin");
cy.visit("/").then(() => {
cy.get("#email").type("test#email.com");
cy.get("#password").type("123456");
cy.get("button[type=submit]").click();
cy.wait("#byPassLogin").then(() => {
cy.url().should("include", "/app");
});
});
and used it like this
describe("test account functionality", () => {
const URL = Cypress.env("api_url");
beforeEach(() => {
cy.byPassLogin();
});
it.only("should logout when click on nav bar", () => {
cy.intercept("POST", URL, (req) => {
if (req.body.operationName === "signOut") {
req.reply({
statusCode: 200,
body: {
data: { updateUser: { errors: null, user: { id: "1" } } },
},
});
}
}).as("signOut");
cy.get("#account").click();
cy.get("#logout").click();
cy.wait("#signOut").then(() => {
cy.url().should("include", "/login");
});
});
error:
CypressError: Timed out retrying after 5000ms: cy.wait() timed out waiting 5000ms for the 1st request to the route: byPassLogin. No request ever occurred.
any help would be appreciated
cypress version: 8.4.1
react: 18

It seems that you should reverse the order of commands in the beforeEach()
beforeEach(() => {
cy.byPassLogin();
cy.visit("/");
})
There is nothing in the command flow between cy.intercept(...).as("byPassLogin") and cy.wait("#byPassLogin") except the signOut intercept.
As you probably know cy.intercept() is a command to set up a listener on the network requests, but it does not trigger any event in the app.
The sequence for intercepting is
set up the intercept listener
trigger the request (e.g cy.visit() or .click())
wait for the alias

Thanks to #fody I succeed to manage the issue, to find the issue I used a workflow similar to this to record my testing in dashboard.cypress.io, then I found it does not post form data to the correct endpoint URL, actually since the URL has been defined in env I needed to define it in CI as well.
That's it.
It was working since I had the env in local.

Related

onNotificationOpenedApp & getInitialNotification don't fire anymore on IOS (expo)

async function onMessageReceived({ title, body, orderId }: { title: string; body: string; orderId?: string }) {
await notifee.createChannel({
id: "main",
name: "Main",
sound: "default",
vibration: false,
importance: AndroidImportance.HIGH,
});
await notifee.displayNotification({
title,
body,
android: {
channelId: "main",
badgeCount: 0,
},
ios: {
badgeCount: 0,
sound: "default",
},
});
notifee.onBackgroundEvent(({ type, detail }) => {
if (type === EventType.PRESS && orderId && isAuthenticated) {
navigate("order", { id: Number(orderId) });
}
});
}
React.useEffect(() => {
messaging().onNotificationOpenedApp(async (remoteMessage) => {
Alert.alert("Notification caused app to open from background state:", JSON.stringify(remoteMessage));
});
messaging().getInitialNotification().then(async (remoteMessage) => {
Alert.alert("Notification caused app to open from background state:", JSON.stringify(remoteMessage));
});
const unsubscribe = messaging().onMessage(async (remoteMessage) => {
// console.log({ remoteMessage });
// Alert.alert("A new FCM message arrived!", JSON.stringify(remoteMessage));
await onMessageReceived({
title: remoteMessage.notification?.title ?? "",
body: remoteMessage.notification?.body ?? "",
orderId: remoteMessage.data?.orderId,
});
});
return () => {
unsubscribe();
};
}, []);
This is my logic for handling the events
This is my code for the setBackgroundMessageHandler:
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
console.log("Message handled in the background!", remoteMessage);
// you could do things like incrementing badge count, doesnt work tho
await notifee.incrementBadgeCount();
});
It appears the setBackgroundMessageHandler doesn't fire.
On android the onNotificationOpenedApp & getInitialNotification works perfect but on ios it stopped working. It worked before, but now i can't get it working anymore.

How get user profile link of facebook?

I am using react-native-fbsdk and tried to get data of user while user is logging in my app using facebook.
I am able to get name,first_name,last_name,email and picture but I also want profile link of user profile of facebook.
I have permssion of user_link.
Can someone explain where I am going or what I need to get the profile url.
LoginManager.logInWithPermissions(['email', 'public_profile', 'user_link'])
.then(
login => {
AccessToken.getCurrentAccessToken().then(data => {
const accessToken = data.accessToken.toString();accessToken);
this.getInfoFromToken(accessToken);
});
})
getInfoFromToken = token => {
const PROFILE_REQUEST_PARAMS = {
fields: {
string: 'id,name,first_name,last_name,email,picture',
},
};
const profileRequest = new GraphRequest(
'/me',
{ token, parameters: PROFILE_REQUEST_PARAMS },
(error, user) => {
if (error) {
//this.setState({ userInfo: error }, () => this.getData())
console.log('login info has error: ' + error);
} else {
this.setState({ userInfo: user }, () =>
console.log('result:', user);
}
},
);
new GraphRequestManager().addRequest(profileRequest).start();
};

Facebook and Google requires two login click to login with Firebase Auth

I have React web application with firebase auth (mail, Facebook, Google).
Google and Facebook work only after 2 login clicks.
The code is equal, just the provider is different.
import React from 'react';
import firebase from "firebase/app";
import { app } from "../../../../config/firebase";
const signupWithGoogle = (user, userInfo)=>{
app.firestore().collection('users').doc(user.uid).set({
firstName: userInfo.profile.given_name,
lastName: userInfo.profile.family_name});
const batch = app.firestore().batch();
const initData = [
{ Applied: { positionIds: [], title: 'Applied' } },
{ Contract: { positionIds: [], title: 'Contract' } },
{ Denied: { positionIds: [], title: 'Denied' } },
{ InProgress: { positionIds: [], title: 'In Progress' } },
{ ReceivedTask: { positionIds: [], title: 'Received Task' } },
];
initData.forEach((doc) => {
const docRef = app
.firestore()
.collection('users')
.doc( user.uid)
.collection('columns')
.doc(Object.keys(doc)[0]);
batch.set(docRef, Object.values(doc)[0]);
});
const batchCommit= batch.commit();
return batchCommit;
}
export const googleLogin = async (
history
) => {
var provider = new firebase.auth.GoogleAuthProvider();
await firebase.auth()
.signInWithPopup(provider)
.then( resp => {
let {user, credential,additionalUserInfo: userInfo} = resp;
if (userInfo.isNewUser) signupWithGoogle(user, userInfo);
}).then(()=>
history.push('/')
)
.catch((error) => {
console.error(error.message);
});
};
I saw this question, but didn't help.(Firebase Authentication Requires Two 'Login' Calls)
I had the same problem with Firebase Authentication with Facebook, I had to register two times to make it works.
The problem was in my HTLM, I used a form.
I changed for a simpler code, and it worked.
While waiting for where you call your function from, as your issue would relate to improper state management, here are some edits you can make to the code you have shared so far to squash some problems that it has.
In your signupWithGoogle function, you create a floating promise that should be included in the write batch that you use to create the /users/{userId}/columns collection. Because you use Object.keys(doc)[0] and Object.values(doc)[0], you should consider using an array of [docId, docData] pairs or a JSON-like object structure like so:
// prepare data to add to the user's columns collection
const initColumnsData = {
Applied: { positionIds: [], title: 'Applied' },
Contract: { positionIds: [], title: 'Contract' },
Denied: { positionIds: [], title: 'Denied' },
InProgress: { positionIds: [], title: 'In Progress' },
ReceivedTask: { positionIds: [], title: 'Received Task' }
};
// queue columns data upload
Object.entries(initColumnsData)
.forEach(([docId, docData]) => {
const docRef = userDocRef
.collection('columns')
.doc(docId);
batch.set(docRef, docData);
});
As you mentioned that a lot of your code is shared aside from the provider implementation, you should consider extracting the common code from those functions:
const initUserData = (user, userDocData) => {
// init write batch
const batch = app.firestore().batch();
// init ref to user data
const userDocRef = app.firestore().collection('users').doc(user.uid);
// queue user data upload
batch.set(userDocRef, userDocData);
// prepare data to add to the user's columns collection
const initColumnsData = {
Applied: { positionIds: [], title: 'Applied' },
Contract: { positionIds: [], title: 'Contract' },
Denied: { positionIds: [], title: 'Denied' },
InProgress: { positionIds: [], title: 'In Progress' },
ReceivedTask: { positionIds: [], title: 'Received Task' }
};
// queue columns data upload
Object.entries(initColumnsData)
.forEach(([docId, docData]) => {
const docRef = userDocRef
.collection('columns')
.doc(docId);
batch.set(docRef, docData);
});
// make the changes
return batch.commit();
}
const initUserDataForGoogle(user, userInfo) {
return initUserData(user, {
firstName: userInfo.profile.given_name,
lastName: userInfo.profile.family_name
});
}
const initUserDataForFacebook(user, userInfo) {
return initUserData(user, {
firstName: /* ... */,
lastName: /* ... */
});
}
When exporting a function to be called elsewhere, avoid causing "side effects" (like navigating using the History API) and don't trap errors (using .catch() without rethrowing the error). The calling code should handle the result and any errors itself.
export const loginWithGoogle = async () => {
const provider = new firebase.auth.GoogleAuthProvider();
return firebase.auth()
.signInWithPopup(provider)
.then(async resp => {
const {user, credential, additionalUserInfo: userInfo} = resp;
if (userInfo.isNewUser)
await initUserDataForGoogle(user, userInfo);
return user;
});
};
Then in your components, you'd use:
setLoading(true);
/* await/return */ loginWithGoogle()
.then(() => {
history.push('/');
// or
// setLoading(false)
// then do something
})
.catch((err) => {
console.error("loginWithGoogle failed: ", err);
setLoading(false);
setError("Failed to log in with Google!"); // <- displayed in UI to user
});

Accessing the values from Promise inside useEffect hook

const useOnfidoFetch = (URL) => {
useEffect(() => {
const appToken = axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const id = json_data.applicant_id;
const token = json_data.onfido_sdk_token;
return {id, token};
});
if (appToken) {
console.log('this is working!');
OnfidoSDK.init({
// the JWT token you generated above
token: null,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}
}, [URL]);
}
export default function() {
const URL = `${transmitAPI}/anonymous_invoke?aid=onfido_webapp`;
const result = useOnfidoFetch(URL, {});
return (
<div id={onfidoContainerId} />
);
}
I have refactored this dozens of times already, I am getting back some values from the appToken Promise, but I need to provide the token value from that Promise to that token property inside of Onfido.init({}) and I need to provide the id to the applicant_id property and I continue to get undefined.
If you need the token for something else as well, then i would suggest storing it in useState, and triggering OnfidoSDK.init when the value of that state changes.
Like this:
const useOnfidoFetch = (URL) => {
const [token, setToken] = useState();
useEffect(() => {
axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const token = json_data.onfido_sdk_token;
setToken(token);
})
}, [URL])
useEffect(() => {
if (!token) return;
OnfidoSDK.init({
// the JWT token you generated above
token,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}, [token]);
}
Move the entire if(appToken){ ... } inside the body of the second .then((json_data) => { ... })
Something like this:
const useOnfidoFetch = (URL) => {
useEffect(() => {
const appToken = axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const id = json_data.applicant_id;
const token = json_data.onfido_sdk_token;
// Here. This code will be executed after the values are available for id and token
if (appToken) {
console.log('this is working!');
OnfidoSDK.init({
// the JWT token you generated above
token: null,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}
return {id, token};
});
// In here the promise is not yet finished executing, so `id` and `token` are not yet available
}, [URL]);
};
export default function() {
const URL = `${transmitAPI}/anonymous_invoke?aid=onfido_webapp`;
const result = useOnfidoFetch(URL, {});
return (
<div id={onfidoContainerId} />
);
}
For better readability, you could also move the if(appToken){ ... } block inside a separate function that takes id, token as arguments, which you can call from inside the promise.then block.

TypeError: http is not a function in Jest framework

I am using React.js as my JavaScript framework, where I have installed Jest and using pact (CDC) for unit test cases and while I run the command npm run test the spec.js file is failing and throwing an error like this
TypeError: http is not a function
This is criteria-managementcomponent.spec.js file
const path = require('path')
const pact = require('pact')
const API_PORT = process.env.API_PORT || 9123
const {
fetchProviderData, getCriteriaManagementComponent, criteriaManagementComponent
} = require('../client')
// Configure and import consumer API
// Note that we update the API endpoint to point at the Mock Service
const LOG_LEVEL = process.env.LOG_LEVEL || 'WARN'
const provider = pact({
consumer: 'Web Login',
provider: 'Web API',
port: API_PORT,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
dir: path.resolve(process.cwd(), 'pacts'),
logLevel: LOG_LEVEL,
spec: 2
})
describe('Started Testing Pact', () => {
beforeEach((done) => {
return provider.setup().then(() => done());
});
afterEach((done) => {
return provider.finalize().then(() => done())
})
describe('criteriaManagement', () => {
beforeEach(() => {
let criteriaManagement = {
uponReceiving: 'wrong criteriaManagement',
state: 'Code OK',
withRequest: {
method: 'GET',
path: '/api/criteria',
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: {
code: "string",
context: {},
message: "string",
payload: [
{
country: "string",
createdBy: "string",
displayOrder: 0,
id: "string",
lastModifiedBy: "string",
name: "string",
translations: {},
warehouse: "string"
}
]
}
}
};
return provider.addInteraction(criteriaManagement)
})
afterEach(() => {
return provider.verify()
})
it('Login test', done => {
criteriaManagementComponent().then((res) => {
expect(res.code).not.toBeNull();
expect(res.context).toBeDefined();
expect(res.message).not.toBeNull();
expect(res.payload.country).not.toBeNull();
expect(res.payload.createdBy).not.toBeNull();
expect(res.payload.displayOrder).toBe(0);
expect(res.payload.id).not.toBeNull();
expect(res.payload.lastModifiedBy).not.toBeNull();
expect(res.payload.translations).toBeDefined();
expect(res.payload.name).not.toBeNull();
expect(res.payload.warehouse).not.toBeNull();
});
done();
})
});
})
This is client.js file for criteria-managementcomponent.spec.js file
const request = require('superagent')
const API_HOST = process.env.API_HOST || 'http://localhost'
const API_PORT = process.env.API_PORT || 9123
const API_ENDPOINT = `${API_HOST}:${API_PORT}`
// Fetch provider data
const fetchProviderData = (submissionDate) => {
return request
.get(`${API_ENDPOINT}/provider`)
.query({ validDate: submissionDate })
.then((res) => {
console.log("Response :: ", res.body);
return {
count: 100 / res.body.count,
date: res.body.date
}
})
}
const getCriteriaManagementComponent = (accessToken, expiresIn) => {
return request
.post(`${API_ENDPOINT}/api/criteria`)
.then((res) => {
return {
code : res.body.code
}
})
}
const criteriaManagementComponent = () => {
// const request = require('request')
console.log("End Point =========> ", `${API_ENDPOINT}/api/criteria`)
return request.get(`${API_ENDPOINT}/api/criteria`).then((res) => {
return {
code : res.body.code,
context:res.body.context,
message:res.body.message,
payload:res.body.payload,
profile:res.body.payload.profile,
successful : res.body.successful
}
})
};
module.exports = {
fetchProviderData, getCriteriaManagementComponent, criteriaManagementComponent
}
It's hard to know where things are going wrong without a line number in the stack trace, however one thing is for certain - you're using a very old version of pact.
You should be using #pact-foundation/pact now.
As for the http issue, is it possible you have any mocks setup that are interfering? The issue seems unrelated to pact.
Lastly, if you could provide a reproducible github repo that others could download and run, you could probably get even better help.

Resources