Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cy.login): login from different users using cy.login() #14

Closed
aviyam1811 opened this issue Feb 3, 2019 · 23 comments
Closed

feat(cy.login): login from different users using cy.login() #14

aviyam1811 opened this issue Feb 3, 2019 · 23 comments
Labels
enhancement New feature or request

Comments

@aviyam1811
Copy link

Does the framework support login via different users?
I have several roles in my app and I want to test each one of them.
Can I declare on different TEST_UIDs in the config in use different users in the tests or there is a workaround for this?

@prescottprue
Copy link
Owner

It is not currently supported, but this is a feature I for sure want to add. What are you thinking for how to select it? Maybe passing the param name into cy.login like cy.login('TEST_UID2') ?

@aviyam1811
Copy link
Author

Yeah I think it's good.
Maybe add users section in the config so it looks like:

{
    "TEST_UIDS": [
         "role1": <some_uid>,
         "role2": <some_uid>
]}

And then use it like cy.login("role1")

@prescottprue
Copy link
Owner

Oh, I really like that idea! Much reads much more clearly. We may want to make that an object though, so we can select by key (I think that is what you actually meant to put):

{
  "TEST_UIDS": {
    "role1": "<some_uid>",
    "role2": "<some_uid>"
  }
}

Cool, I'll add this as a feature to build. Feel free to make a PR if you get a chance, if not I will try to get to it when I can.

@aviyam1811
Copy link
Author

I tried to fix it but currently I don't have time for it.
Could you please estimate an time for this feature to be supported?

@prescottprue prescottprue added the enhancement New feature or request label Feb 24, 2019
@prescottprue
Copy link
Owner

When starting to investigate I realized that this could create some difficult if multiple logins happen without logout happening first. It is tough to estimate a time, I'll continue to investigate and post updates here.

@TapaiBalazs
Copy link

I just add my two cents here:
I have a really small app with only user and admin roles, and I serve one app instance on localhost:1111 for the simple user and on localhost:2222 for the admin user tests. They can run parallelly, requires some small effort for visiting the proper URL in the actual tests.
I know it might not be that cost effective or ideal to run an instance for every role just to be able to test them parallelly, but that could be a workaround.

@dhair-seva
Copy link
Contributor

I'd be happy to help chip in on making a PR for this. I need this for my team. We have about 4 portals into our app (client with 5 roles, tester portal, captain portal and super portal). I can easily use the current functionality for tester, captain and super. However, the client portal with 5 roles will prove to be difficult.

Couldn't we check the uid that is currently logged in, and if that uid does not match the uid we want to login with, then we logout and then do the login process?

@dhair-seva
Copy link
Contributor

dhair-seva commented Aug 20, 2019

After thinking about it more, I'm thinking that using:

Cypress.env("TEST_UID", TestUsers.superUid)

and then logging out and back in, when needed will give me what I need. Its not ideal and love the idea of TEST_UID being an object. But this might be a good workaround

@prescottprue prescottprue changed the title Login from different users by cy.login() feat(cy.login): login from different users using cy.login() Oct 9, 2019
@Codediggar
Copy link

Any update on this please.
We use a dedicate function id to run E2E testcases and want to use the same ID in my local machine.
When I run it always takes my ID instead of the Functional ID , even after overriding the login with options.
It gets me a token correctly , auth works but the UI in electron using my ID to login, how to logout and use different ID?

@fabiocarneiro
Copy link

I would like to have it like this:

cy.firebaseSignUp(email, password)
cy.firebaseSignIn(email, password)

other authentication options would also be interesting

I think there is some coupling between two independent problems here:

  • Obtaining credentials or managing for an already registered user by using admin access
  • Registering and Authenticating a user that you have credentials for

Basically, my goal is to have the testing suite to create a new user every time I run the test, avoiding the state of previous tests to influence the result, or requiring a clean up of the state after each run.

The current library implementation forces the usage of a single user or a specific authentication method. IMHO, that is not ideal.

prescottprue pushed a commit to prescottprue/firebase-tools-extra that referenced this issue Feb 8, 2020
* feat(core): add createCustomToken command [14 of cypress-firebase](prescottprue/cypress-firebase#14)
prescottprue pushed a commit that referenced this issue Feb 8, 2020
* feat(login): add support for passing UID - #14
@prescottprue
Copy link
Owner

prescottprue commented Feb 8, 2020

Work for this is being included in v0.10.0 - it calls a firebase-tools-extra command to generate the custom token dynamically then logs in.

@fabiocarneiro signing in with credentials such as email and password was initially skipped since that would mean either exposing the credentials in the test code or passing them through from the environment. I'm open to the idea of creating custom commands for doing this, but as you said it is really part of a different concern.

In this issue I'm going to address passing a UID to login to a different user (logging in through custom auth token) - we can address creating user's during the test as a different feature. I made #88 to track that.

Something to note is that you aren't locked into a specific auth method per say - you can login any way you want as a user of your app, then grab the UID from the Auth tab of Firebase. Then if you use that UID, it will login with a custom token, but that still works with accounts that were created by other auth methods. This has been preferred for my apps so we aren't building up user accounts over time just from tests running.

prescottprue added a commit to prescottprue/firebase-tools-extra that referenced this issue Feb 8, 2020
* feat(core): add createCustomToken command [14 of cypress-firebase](prescottprue/cypress-firebase#14)
@fabiocarneiro
Copy link

fabiocarneiro commented Feb 8, 2020

Something to note is that you aren't locked into a specific auth method per say - you can login any way you want as a user of your app, then grab the UID from the Auth tab of Firebase

@prescottprue what I don't like here is the assumption that you will use one user for all your tests and that the authentication happens outside of Cypress, and then the token is used afterwards.

@prescottprue
Copy link
Owner

@fabiocarneiro The logic that is being added in v0.10.0 will allow for logging into different users by passing their UID into cy.login - since the token will be generated on the fly this will allow the usage of multiple users in tests

@fabiocarneiro
Copy link

@fabiocarneiro The logic that is being added in v0.10.0 will allow for logging into different users by passing their UID into cy.login - since the token will be generated on the fly this will allow the usage of multiple users in tests

Does that use the admin api?

@prescottprue prescottprue mentioned this issue Feb 12, 2020
prescottprue added a commit that referenced this issue Feb 12, 2020
* feat(login): add support for passing UID - #14 
* feat(types): update type of `login` function to include optional `uid` param - #14 
* feat(tests): add tests for emulator support - #73 
* chore(deps): update [firebase-tools-extra version](https://github.com/prescottprue/firebase-tools-extra/releases/tag/v0.5.0) for custom token generation used in new `login` functionality - #73
@prescottprue
Copy link
Owner

It does - it calls createCustomToken internally

@prescottprue
Copy link
Owner

Closing since this if a feature within v0.10.0. Anyone that tries it out - reach out if it doesn't work as expected

@fabiocarneiro
Copy link

fabiocarneiro commented Feb 13, 2020

It does - it calls createCustomToken internally

@prescottprue The only concern I have with that is that it is assuming I do have specific accounts that I want to test with, and I will hardcode the ids on my code. This adds some fragility, as there can be different states based on these accounts on different runs and the accounts can be modified/removed outside of the scope of the application you are testing.

It would still be nice to have support to create an account during the test. Also, cy.login could be a bit more specific to firebase. Someone new looking at the code will have no clue where this login comes from. For those two reasons I proposed:

cy.firebaseSignUp({email, password})
cy.firebaseSignIn({email, password})

PS1: cy.login could be deprecated but still be working for the next versions
PS2: The SignUp/SignIn approach has the advantage of not requiring admin API.

@Nick-Mazuk
Copy link

It does - it calls createCustomToken internally

@prescottprue The only concern I have with that is that it is assuming I do have specific accounts that I want to test with, and I will hardcode the ids on my code. This adds some fragility, as there can be different states based on these accounts on different runs and the accounts can be modified/removed outside of the scope of the application you are testing.

It would still be nice to have support to create an account during the test. Also, cy.login could be a bit more specific to firebase. Someone new looking at the code will have no clue where this login comes from. For those two reasons I proposed:

cy.firebaseSignUp({email, password})
cy.firebaseSignIn({email, password})

PS1: cy.login could be deprecated but still be working for the next versions
PS2: The SignUp/SignIn approach has the advantage of not requiring admin API.

I totally agree and this is really what I was looking for. Ideally, each test should be isolated from one another, and having hard-coded UIDs destroys that isolation. Because one test may update the user profile (e.g. custom claims), and another may depend on the auth claim not changing. Running the tests in the reverse order causes a failing test.

Alternatively, if you don't use that format, it would be okay to have:

cy.createUser({ uid, email, password, customClaims, etc })
cy.removeUser(uid)

@Nick-Mazuk
Copy link

@fabiocarneiro

Found a workaround to creating users in the code and cleaning up users afterward. You can essentially add your own cypress commands to create and log-in users by using the front-end firebase code. And if you use firebase's REST API, you can delete all the user accounts after each test.

https://firebase.google.com/docs/reference/rest/auth#section-auth-emulator-clearaccounts

Here are the commands I added to commands.ts (should still work for normal JS). Note that this was only tested using the firebase emulators.

// commands.ts
Cypress.Commands.add('createUser', (email: string, password: string) => {
    Cypress.log({
        displayName: 'createUser',
        consoleProps: () => {
            return { email, password }
        },
    })
    return firebase.auth().createUserWithEmailAndPassword(email, password)
})

Cypress.Commands.add('loginWithEmail', (email: string, password: string) => {
    Cypress.log({
        displayName: 'loginWithEmail',
        consoleProps: () => {
            return { email, password }
        },
    })
    return firebase.auth().signInWithEmailAndPassword(email, password)
})

Cypress.Commands.add('deleteAllUsers', (email: string) => {
    Cypress.log({
        displayName: 'deleteAllUsers',
        consoleProps: () => {
            return { email }
        },
    })
    return cy.request({
        url: 'http://localhost:9099/emulator/v1/projects/{project-id}/accounts',
        method: 'DELETE',
        auth: { bearer: 'owner' },
    })
})

To use these:

// hello-world.test.ts

it('this creates, signs in, and deletes a user', () => {
    cy.createUser('email@example.com', 'useabetterpassword')
    cy.loginWithEmail('email@example.com', 'useabetterpassword')
    cy.deleteAllUsers()
})

@fabiocarneiro
Copy link

fabiocarneiro commented Jan 15, 2021

@Nick-Mazuk Looks good and thanks for sharing. I didn't use cypress recently but will for sure come back to it at some point. This should be added to the library. Maybe open a PR?

@felippepuhle
Copy link

Is it a good idea exposing the auth interface on the window so I can access directly inside Cypress and call createUserWithEmailAndPassword so user will be already logged in after that? Also, our app uses signInWithPhoneNumber, is there a way I can create a whitelisted user so I can use a static code for the testing step?

@prescottprue
Copy link
Owner

@felippepuhle You shouldn't need to be creating the user through email/password for things to work - just using a new or existing auth user's UID in cy.login will log you in as that user. So no need to call createUserWithEmailAndPassword just call cy.login('someUID') where 'someUID' is the ID you would like to assign

If you are running your tests against a full Firebase project instead of the emulators, you can also create a user through your app the normal way, then use that UID with cy.login even if the account is an email/password provider account or a phone number provider account.

Testing the UI for loggin in with phone number is a different case - for those you will want to have a known user within your auth and actually authenticate with Firebase through the UI instead of using cy.login (but again, this is only for testing the actual login functionality of your app, not other pages, even if they require auth)

If you need more info, feel free to open a discussion 😄

@felippepuhle
Copy link

@prescottprue thanks for the explanation and tips!

Yeah, we're running our tests against a full Firebase project... We want to make our tests as isolated as possible (we're running them in parallel in more than 1 browser, and we already fel in some race conditions where 2 browsers were testing the same screen with the same user and the test fails due to that - wrong values on assertions).

Our signup flow is kinda slow (there're a lot of screens, that's the reason we don't want to create a new user through the UI each test), I'm considering using this project to handle the authentication but I'm missing this signup stuff... is there a way we can implement both createUser and deleteUser? I can help with that, just need some help to better understand the project.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants