0

I use supabase for my express js backend. This is how I have defined my supabase client:

const config = require('../config/env');
const supabase = require('@supabase/supabase-js');
const supabaseClient = supabase.createClient(
  config.supabase.url,
  config.supabase.key,
  {
    auth: {
      autoRefreshToken: false,
      persistSession: false,
      detectSessionInUrl: false,
    },
  }
);

module.exports = supabaseClient;

An example call to the database can be something like this:

 const query = supabaseClient
    .from('items')
    .select('*')
    .eq('id', id);


  const { data, error } = await executeWithRetry(async () => await query);

I am writing some tests to test this logic and I would like to mock the supabase calls. This is my jest mock for supabase:

jest.mock('@supabase/supabase-js', () => {
  return {
    createClient: jest.fn().mockImplementation(() => {
      return {
        from: jest.fn().mockReturnThis(),
        select: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          execute: jest.fn().mockImplementation(() => {
            return Promise.resolve({
              data: [{ id: 'some-uuid-text' }],
              error: null,
            });
          }),
        })),
      };
    }),
  };
});

and this is the test

describe('userPermissions', () => {
  it('should fetch user permissions', async () => {
    const email = '[email protected]';
    const result = await userPermissions({ email });
    expect(result).toEqual([{ role_id: 'mockedRoleId' }]);
  });
});

The userPermissions method has a query that attempts to fetch data from supabase which keeps on responding with undefined data and undefined error. My expectation is that it should respond with what's in my execute part of the mock

return Promise.resolve({
              data: [{ id: 'some-uuid-text' }],
              error: null,
            });

What could be causing the issue?

1 Answer 1

4

The logic that I was using seemed correct but supabase doesn't have the execute function as part of its functions that's why the supabase calls were not responding with any response because I wasn't passing any data or error. Updating the mock with data and error to this fixes the issue:

jest.mock('@supabase/supabase-js', () => {
  return {
    createClient: jest.fn().mockImplementation(() => {
      return {
        from: jest.fn().mockReturnThis(),
        select: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          data: [
            {
              id: 'mockedRoleId',
              org_users: [{ id: 'mockUserId', status: 'active' }],
            },
          ],
          error: null,
        })),
        update: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          select: jest.fn().mockReturnThis(),
          data: [
            {
              id: 'mockedRoleId',
              org_users: [{ id: 'mockUserId', status: 'active' }],
            },
          ],
          error: null,
        })),
      };
    }),
  };
});

To make the mocks more reusable, I went with a variant where I could define the expected data for every test, as seen below:

jest.mock('@supabase/supabase-js', () => {
  let testData = [
    {
      id: 'mockedRoleId',
      org_users: [{ id: 'mockUserId', status: 'active' }],
    },
  ];

  return {
    createClient: jest.fn().mockImplementation(() => {
      return {
        from: jest.fn().mockReturnThis(),
        select: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          data: testData, // Use the data variable here
          error: null,
        })),
        update: jest.fn().mockImplementation(() => ({
          eq: jest.fn().mockReturnThis(),
          in: jest.fn().mockReturnThis(),
          is: jest.fn().mockReturnThis(),
          order: jest.fn().mockReturnThis(),
          gte: jest.fn().mockReturnThis(),
          lte: jest.fn().mockReturnThis(),
          select: jest.fn().mockReturnThis(),
          data: testData, // Use the data variable here
          error: null,
        })),
      };
    }),
    setTestData: (newData) => {
      testData = newData;
    },
  };
});

This is how the mock is used in the actual tests:

describe('activateUser', () => {
  beforeEach(() => {
    const { setTestData } = require('@supabase/supabase-js');
    setTestData([]);
  });

  test(
    'should throw in error when no active user exists',
    async () => {
      const userEmail = '[email protected]';
      const userProfileId = 'some-random-uuid';
      const tenantId = 'some-random-uuid';

      expect(() => {
        activateUser({ userProfileId, userEmail, tenantId });
      }).toThrowError('User does not exist');
    }
  );

  it('should activate user account', async () => {
    const { setTestData } = require('@supabase/supabase-js');
    setTestData([
      {
        id: 'mockedRoleId',
        org_users: [{ id: 'mockUserId', status: 'active' }],
      },
    ]);
    const userEmail = '[email protected]';
    const userProfileId = 'ab3b2cb0-c032-42d8-b1d2-c78de679189f';
    const tenantId = 'a76b1802-0acb-4e9c-a157-d4c8239f6c2a';

    activateUser({ userProfileId, userEmail, tenantId });
  });
});
Sign up to request clarification or add additional context in comments.

1 Comment

Nicely done. Your solution inspired me to use the same logic.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.