Cypress using actions from Pinia Vue3
Asked Answered
B

1

8

I was learning some cypress from this video: https://www.youtube.com/watch?v=03kG2rdJYtc I'm interested with he's saying at 29:33: "programatic login" But he's using vue2 and Vuex.

My project is created with Vite and the state management is Pinia. So how can I do a programatic login using the pinia action?

For example the welcome logged in user should see dashboard:

describe('Welcome', () => {
  it('logged in user should visit dashboard', () => {
    // login
    cy.visit('/')
    cy.url().should('contain', '/dashboard')
  })
})

And my userStore:

export const useUserStore = defineStore({
  id: 'user',
  state: () => ({
    username: ref(useLocalStorage('username', null)),
  }),
  getters: {
    isLoggedIn: (state) => state.username !== null,
  },
  actions: {
    login(username, password) {
      return useAuthLoginService(username, password)
        .then((response) => {
          this.username = response.username
        })
        .catch((error) => {
          return Promise.reject(new Error(error))
        })
    },
  },
})

How can I call the login action on the cypress test? For now as a workaround I'm writing on a localstorage like:

localStorage.setItem('username', 'user')

And it works fine, because userStore catch this item from localstorage and passes like it's logged in... But I don't like this solution, seems fragile, and I'd like to use the action which is made for login users.

Another thing I tried is adding the app variable inside window but it doesn't work for me... don't understand why...

on main.js

The video shows that code:

const vue = new Vue({...})
if(window.Cypress){
  window.app = app
}

In my case it's:

const app = createApp(App)
if(window.Cypress){
  window.app = app
}

But in cypress tests the window.app it's undefined... I don't know how I would access to userStore using this... like it was vuex.

Butts answered 5/4, 2022 at 13:34 Comment(0)
M
8

Using the Pinia demo app as an example:

The store is initialized in App.vue. Add a reference to the newly created store(s) for Cypress to use

export default defineComponent({
  components: { Layout, PiniaLogo },

  setup() {
    const user = useUserStore()
    const cart = useCartStore()
 
    if (window.Cypress) {
      window.store = {user, cart)   // test can see window.store
    }
    ...

In the test

let store;  

describe('Pinia demo with counters', () => {

  beforeEach(() => {
    cy.viewport(1000, 1000)
    cy.visit(`http://localhost:${PORT}`)
      .then(win => store = win.store)       // get app's store object 
  })

  it('works', () => {
    cy.wait(500) // wait for the JS to load
      .then(() => store.cart.addItem('Cypress test item'))  // invoke action
      .then(() => {
        const item1 = store.cart.items[0]                   // invoke getter
        cy.wrap(item1)
          .should('have.property', 'name', 'Cypress test item')  // passes
      })

The login action is asynchronous, so return the promise to allow Cypress to wait.

// user.js

async login(user, password) {
  const userData = await apiLogin(user, password)

  this.$patch({
    name: user,
    ...userData,
  })
  return userData        // this returns a promise which can awaited
},
// main.spec.js

describe('Pinia demo with counters', () => {
  beforeEach(() => {
    cy.viewport(1000, 1000)
    cy.visit(`http://localhost:${PORT}`).then(win => {
      store = win.store

      // default name in store before login
      cy.wrap(store.user.name).should('eq', 'Eduardo')

      // logging in
      store.user.login('ed', 'ed').then(() => {        // wait for API call
        cy.wrap(store.user.name).should('eq', 'ed')
      })
    })
  })

Alternatively, wait for the name to change on the page

// main.spec.js

cy.visit(`http://localhost:${PORT}`).then(win => {
  store = win.store

  // default name in store
  cy.wrap(store.user.name).should('eq', 'Eduardo')

  // logging on
  store.user.login('ed', 'ed')  
  cy.contains('Hello ed')              // waits for name on page to change
    .then(() => {          
      cy.wrap(store.user.name).should('eq', 'ed')
    })
})
Maupin answered 25/4, 2022 at 3:52 Comment(3)
Yes this is working thank you. I was trying to add it on main.js but I tried on App.vue and all worksButts
Looks like an anti pattern to me. You don't want to add 30 stores to window just for testing purpose. There must be a better way.Reverent
The code is only active during testing - see if (window.Cypress)...Webber

© 2022 - 2024 — McMap. All rights reserved.