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

How to mock useAuth in Vitest ? #596

Open
ErinTran15 opened this issue Dec 3, 2023 · 6 comments
Open

How to mock useAuth in Vitest ? #596

ErinTran15 opened this issue Dec 3, 2023 · 6 comments
Labels
documentation A change to the documentation p4 Important Issue

Comments

@ErinTran15
Copy link

Environment

No response

Reproduction

No response

Describe the bug

I've been trying to do vi.mock and unplugin-auto-import to mock it but none of them works
when I config it in unplugin-auto-import in vitest.config.ts like this

 imports: [
      'vue',
      'vue-router',
      {
        '@sidebase/nuxt-auth: [
          "useAuth"
        ],
      },
    ],

surely it won't work because its the wrong path
but when I import it with the correct path like in auto import path it will give me error:

 imports: [
      'vue',
      'vue-router',
      {
        '@sidebase/nuxt-auth/dist/runtime/composables/authjs/useAuth': [
          "useAuth"
        ],
      },
    ],

Missing "./dist/runtime/composables/authjs/useAuth" specifier in "@sidebase/nuxt-auth" package

pls is there any other ways I could mock it ? Really appreciate if you guys can help me

Additional context

No response

Logs

No response

@zoey-kaiser
Copy link
Member

Hi! Mocking is generally a bit harder to accomplish inside of the module, as everyone has different session data. The way we handled it internally is as follows:

1.) Inside of /tests/mock/setup.ts we created a new "mocked" version of nuxt-auth:

import { createResolver, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup: (_options, nuxt) => {
    const { resolve } = createResolver(import.meta.url)
    const pathToMocks = resolve('./nuxt-auth.ts')

    nuxt.hook('imports:extend', (_imports) => {
      _imports.push({ name: 'useAuth', from: pathToMocks })
    })

    nuxt.hook('nitro:config', (nitroConfig) => {
      if (!nitroConfig.alias) {
        throw new Error('Alias must exist at this point, otherwise server-side cannot be mocked')
      }
      nitroConfig.alias['#auth'] = pathToMocks
    })
  },
})
  1. Inside of this file we referenced a mocked version of the composable used inside of NuxtAuth written in /tests/mock/nuxt-auth.ts
import { eventHandler } from 'h3'

export const MOCKED_USER = { user: { role: 'admin', email: 'test@example.com', name: 'John Doe' } }

// App-side mocks
export const useAuth = () => ({
  data: ref(MOCKED_USER),
  status: ref('authenticated'),
  getSession: () => MOCKED_USER,
  signOut: () => {},
})

// Server-side mocks
export const getServerSession = () => MOCKED_USER
export const NuxtAuthHandler = () => eventHandler(() => MOCKED_USER)
  1. Inside of our nuxt.config.ts we check if the environment is currently a test, environment. If it is we add this new mocked module into our modules array:
const mockAuthModule = process.env.VITEST ? ['./test/mock/setup.ts'] : []

export default defineNuxtConfig({
  modules: [
    '@sidebase/nuxt-auth',
    ...mockAuthModule,
  ],
}

When running the application in ViTest, the mocked module will overwrite the real NuxtAuth module. This will result in a console warning that the composables are being overwritten, which is fine, as the mock will then work!

If we want to integrate this functionality into the module itself, I only see one issue:
How do we allow people to define their own session data, as everyones session datatype can be different (also between providers). However I hope this quick overview helps you!

@peterbud
Copy link

That's a great summary @zoey-kaiser.

Few suggestions:

  • I believe with the latest Nuxt Test Utils, the best way to mock the useAuth import on the client side is to use mockNuxtImport
  • I think the best option would be to add such mocking setup to the examples/playground as I see this question comes up frequently. Either adding to the default playground, or creating a separate playground folder for that - if you agree with that I can create a PR.

@Klumper
Copy link

Klumper commented Feb 4, 2024

I managed to get it to work with Cypress a while back. Maybe this can be useful information for the Vitest implementation.

Created a class to generate a token which then will be set within the cookies (next-auth.session-token), which is used within the sidebase package.

The token includes the provided user information, which can be extracted within your tests. I used it to specify permissions to test them 😄 .

Note that the user secret should be the same as your configured secret within sidebase.

import { EncryptJWT, type JWTPayload } from 'jose';
import hkdf from '@panva/hkdf';
import User from './User';

export default class Session {
    user: User;

    secret: string;

    accessToken: string;

    expires: Date;

    constructor (secret: string, user?: User) {
        this.secret = secret;
        this.user = user || new User();
        // Set the expiration date to 1 year from now
        this.expires = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000);
    }

    private getDerivedEncryptionKey () {
        return hkdf(
            'sha256',
            this.secret,
            '',
            'NextAuth.js Generated Encryption Key',
            32,
        );
    }

    private async encode (token: JWTPayload) {
        const maxAge = 30 * 24 * 60 * 60; // 30 days
        const encryptionSecret = await this.getDerivedEncryptionKey();
        return new EncryptJWT(token)
            .setProtectedHeader({ alg: 'dir', enc: 'A256GCM' })
            .setIssuedAt()
            .setExpirationTime(Math.round(Date.now() / 1000 + maxAge))
            .setJti('cypress-test')
            .encrypt(encryptionSecret);
    }

    async generateAccessToken () {
        this.accessToken = await this.encode({ user: this.user });
    }
}

@zoey-kaiser
Copy link
Member

Lets add a section to the documentation this these examples!

@pablo-vega-kloeckner-i
Copy link

@zoey-kaiser Where can I fine this information in the documentation?

@zoey-kaiser
Copy link
Member

Hi @pablo-vega-kloeckner-i 👋

We currently do not have any documentation on this, as we are currently rewriting the entire docs (sidebase/docs#180). However, I have outlined our vitest setup in this comment: #596 (comment)

If you have any additional questions to this, feel free to ask them here 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation A change to the documentation p4 Important Issue
Projects
None yet
Development

No branches or pull requests

5 participants