I have a bunch of Cypress tests that are being driven by a fixture file which contains the number of tests I want to run.
As part of my tests. I need to visit a registration link and register a new account.
The problem is that the first time I visit the registration form. It appears fine. But if I go to it again. The new form doesn't show and I only see the regular login form.
I suspect that because I'm running multiple tests from one spec file that Cypress is remembering that I've already visited the page and showing me the log in form.
I know I shouldn't be using the UI to register new accounts. But it's the only solution currently.
/// <reference types="cypress" />
let user;
before(function () {
cy.task("newUser").then((user) => {
user = user;
});
});
const types = require("../fixtures/types");
types.forEach((type) => {
context("Matter Creation", () => {
it("Tests if a Service Agent can create a new matter", () => {
cy.fixture("data").then((data) => {
cy.addNewUser({
userEmail: user.email,
userPassword: user.password,
});
});
});
});
context("User Registration", () => {
it("Tests the registration process from a users perspective", () => {
cy.userRegistration({
userEmail: user.email,
userPassword: user.password,
});
});
it("Tests that users are registered and can sign in", () => {
cy.verifyRegistration({
userEmail: user.email,
userPassword: user.password,
});
});
});
});
Fixed it by moving my before state into my describe block.
It was reusing old data, instead of using new data for every test.
Related
In creating a user settings page, I wanted display current user account email and password and give the user the ability to change both.
I read up on https://guide.meteor.com/accounts.html#displaying-user-data, but I'm still confused about how the data is handled.
Would somebody be able to point me in the right direction? Attached below is a code snippet of how our sign-up page handles the data.
/** Handle Signup submission. Create user account and a profile entry, then redirect to the home page. */
submit = () => {
const { email, password } = this.state;
Accounts.createUser({ email, username: email, password }, (err) => {
if (err) {
this.setState({ error: err.reason });
} else {
this.setState({ error: '', redirectToReferer: true });
}
});
}
I am trying to display information about users in a group in a list. I want to gather additional information about each member which is stored under users/ in my database.
My code manages to gather information but only for the first member. To get the others members data to load, I need to navigate back to a different screen then go back.
Here is what I tried so far:
//members object from group/id/members is passed to this function
//e.g. [{"uid":"abc","type":"admin"},{"uid":"def","type":"member"},{"uid":"ghi","type":"member"}]
getMembersData(members) {
const membersData = {};
Object.keys(members).map((key, i) => {
const member = members[key];
firebase.database().ref(`users/${member.uid}`).on('value', snap => {
membersData[member.uid] = {
'uid': member.uid,
'username': snap.child('username').val(),
'imageUrl': snap.child('imageUrl').val(),
'type': member.type,
};
});
});
this.setState({ membersData });
}
In other words, when I navigate to the members screen, only the information for the first member is rendered when I map this.state.membersData in a View, until I back out from the screen and go back to it.
The code works fine if I cut out the firebase content. How can I fix this issue so that everything loads when I navigate to the screen?
I think you'll want to call setState inside the database callback since the callback is asynchronous and won't be invoked until the data is finally available.
firebase.database().ref(`users/${member.uid}`).on('value', snap => {
membersData[member.uid] = {
'uid': member.uid,
'username': snap.child('username').val(),
'imageUrl': snap.child('imageUrl').val(),
'type': member.type,
};
this.setState(membersData);
});
});
There is no guarantee how long it will take to get the data, so your code should be ready to deal with the fact that it might take a while to arrive (or possibly never at all if there is no internet connection).
You want to call this.setState once all of your database calls have finished. If you only need to download the data once, you can make use of chaining Promises.
Because you are using .map(), you can achieve this by returning the Promise from database requests that use .once('value'). In the code below, the variable named transformPromises is the array of Promises produced by .map() and .once('value'). Once you have this array of promises, you can use Promise.all(promiseArray).then(() => {...} to wait for them all to resolve. In the code below, this is where we then call this.setState() to update the user interface.
Before calling getMembersData() it may be useful to show a throbber while loading the data.
getMembersData(members) {
const membersData = {};
let transformPromises = Object.keys(members).map((key, i) => {
const member = members[key];
return firebase.database().ref(`users/${member.uid}`).once('value')
.then(snap => {
membersData[member.uid] = {
'uid': member.uid,
'username': snap.child('username').val(),
'imageUrl': snap.child('imageUrl').val(),
'type': member.type,
};
});
});
Promise.all(transformPromises)
.then(() => {
// all data was downloaded & added successfully.
this.setState({ membersData });
})
.catch((err) => {
// handle error
console.error(err)
});
}
Note: Based on the shape of members, you could also iterate them using the following code:
let transformPromises = members.map(member => {
return firebase.database().ref(`users/${member.uid}`).once('value')
.then(snap => {
membersData[member.uid] = {
'uid': member.uid,
'username': snap.child('username').val(),
'imageUrl': snap.child('imageUrl').val(),
'type': member.type,
};
});
}
I've made a simple app with phone authentication (sms).
My problem splits to two, the first part is that the verification code (sms) is always wrong somehow (I do get it, however it doesn't pass the confirmation), and the second part (as stated in the title) is that the user can still access the main activities even if authentication failed.
the function is invoked via a button.
the function is :
signIn(){
const appVerifier = this.recaptchaVerifier;
const phoneNumberString = "+972" + this.phoneNumber.substring(1,10);
firebase.auth().signInWithPhoneNumber(phoneNumberString, appVerifier)
.then( confirmationResult => {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
let prompt = this.alertCtrl.create({
title: 'Enter the Confirmation code',
inputs: [{ name: 'confirmationCode', placeholder: 'Confirmation Code' }],
buttons: [
{ text: 'Cancel',
handler: data => { console.log('Cancel clicked'); }
},
{ text: 'Send',
handler: data => {
confirmationResult.confirm(data.confirmationCode)
.then(function (result) {
// User signed in successfully.
this.uid = result.user.uid
this.addUser(this.fullName, this.uid);
console.log(result.user);
// ...
}).catch(function (error) {
console.log("Invalid code") // always getting here
});
}
}
]
});
prompt.present();
}).catch(function (error) {
console.log("SMS not sent")
});
}
UPDATE (app.component)
the decision is made in the constructor of app.component.ts
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
var that = this
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
that.rootPage = TabsPage; // even though auth failed, he comes here
} else {
that.rootPage = LoginPage;
}
});
});
}
I dont see it in your code but anywhere you call a method to push the main App-Page. You only should show the main App-Page after User successfully logged in. If this dont work maybe the user comes inside of your app, because the Firebase function is asynchron.
How can I add extra attributes phone number and address to this data set? It seems like Firebase documentation doesn't specify anything about that.
I have implemented the login, register and update using firebase.auth()
Login :
//Email Login
firebase.auth().signInWithEmailAndPassword(email, password).then(
ok => {
console.log("Logged in User",ok.user);
},
error => {
console.log("email/pass sign in error", error);
}
);
Register:
//Sign Up
firebase.auth().createUserWithEmailAndPassword(email, password).then(
ok => {
console.log("Register OK", ok);
},
error => {
console.log("Register error", error);
}
)
Update:
//User Authentication
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
$scope.data=user;
} else {
// No user, Redirect to login page
}
});
//Save Function
$scope.save=function(values){
$scope.data.updateProfile({
displayName: "Test User",
email: "test#gmail.com",
/* phone: 123412341,
address: "Temp Address",*/
photoURL: "www.example.com/profile/img.jpg"
}).then(function() {
// Update successful.
}, function(error) {
// An error happened.
});
};
As far as I know, you have to manage the users profiles by yourself if you want to have more fields than the default user provided by Firebase.
You can do this creating a reference in Firebase to keep all the users profiles.
users: {
"userID1": {
"name":"user 1",
"gender": "male"
},
"userID2": {
"name":"user 2",
"gender": "female"
}
}
You can use onAuthStateChanged to detect when the user is logged in, and if it is you can use once() to retrieve user's data
firebaseRef.child('users').child(user.uid).once('value', callback)
Hope it helps
This can be done by directly storing your custom data in Firebase Auth as "custom claims" on each user via the Admin SDK on your backend.
Note this can't be done purely client-side, your server (or you can use a Cloud Function as per the linked guide if you don't already have a server/API set up) needs to make a request through the Admin SDK to securely set the data using the admin.auth().setCustomUserClaims() method:
https://firebase.google.com/docs/auth/admin/custom-claims#defining_roles_via_an_http_request
You could write some code that combines data from firebase auth and firestore document and expose that to the app as a single data entity. To take subscriptions and notify that changes to the whole app, you would be better served with event libraries like Rxjs. Bellow, I wrote the example below using a simple library that implements an event bus.
// auth.js
import { publish } from '#joaomelo/bus'
import { fireauth, firestore } from './init-firebase.js'
const authState = {
userData: null
};
fireauth.onAuthStateChanged(user => {
if (!user) {
authState.userData = null;
publish('AUTH_STATE_CHANGED', { ...authState });
return;
}
// we must be carefull
// maybe this doc does not exists yet
const docRef = firestore
.collection('profiles')
.doc(user.uid);
docRef
// 'set' secures doc creation without
// affecting any preexisting data
.set({}, { merge: true })
.then(() => {
docRef.onSnapshot(doc => {
// the first data load
// and subsequent updates
// will trigger this
authState.userData = {
id: user.uid,
email: user.email,
...doc.data()
};
publish('AUTH_STATE_CHANGED', { ...authState });
});
});
});
// some-place-else.js
import { subscribe } from '#joaomelo/bus'
subscribe('AUTH_STATE_CHANGED',
authState => console.log(authState));
You can expand on that in a post I wrote detailing this solution and also talking about how to update those properties. There is too a small library that encapsulates the answer with some other minor features with code you could check.
What I am trying to do here is to implement a functionality on the start-up. I want my user's firebase authentication email variable to set a variable that represents the current user logged into my app?
With the following code the line that sets the user variable works after I click log in but not on page load! The console logs work perfectly on start-up but not the setting of user to the email...
crossfitApp.controller('globalIdCtrl', ["$scope", 'defautProfileData',
function ($scope, defautProfileData) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
//defautProfileData.country;
$scope.authenticated = {
currentUser: 10007,
emailAddress: "",
settings: "",
};
$scope.auth = new FirebaseSimpleLogin(dataRef, function (error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
//Not working
$scope.authenticated.currentUser = user.id;
console.log('User ID: ' + user.id + ', ProvideFr: ' + user.provider + user);
console.log(user);
} else {
console.log($scope.auth);
alert('deuces');
//!Trigger not logged in
}
});
}
]); //GlobaldCtrl
The callback to FirebaseSimpleLogin is not invoked inside the scope of Angular's HTML compiler. Normally, whenever you invoke ng-click, ng-submit, et al, Angular fires $scope.$apply(), which checks for any changes to the bound JavaScript variables and applies those to the DOM elements.
When an event outside of Angular changes a variable, you need to let Angular know by manually triggering a $apply event. The safest way to accomplish this is to use $timeout:
angular.controller('MyCtrl', function($scope, $timeout) {
$scope.auth = new FirebaseSimpleLogin(dataRef, function (error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
$timeout(function() {
$scope.currentUser = user.uid;
});
} else {
console.log('not logged in');
}
});
In general, prefer user.uid to user.id, as it is unique across providers.
A library like AngularFire can save you a lot of trouble, as it abstracts a lot of the complexities of integrating Firebase and Angular.