isaacharrisholt / supawright

A Playwright test harness for E2E testing with Supabase
MIT License
46 stars 2 forks source link

Handle creating auth.users #4

Closed 0xBigBoss closed 8 months ago

0xBigBoss commented 8 months ago

Hey Supawright is working great, but thought I'd bring up a workaround that I'm facing in the hopes that there is an easy way to implement this with supawright. A lot of our tables have auth.users as a foreign key constraint so it forces us to create one before calling supawright.create. See below as an example of what I'm talking about or here in the repo.

import { mergeTests } from '@playwright/test'
import { test as sendAccountTest, expect } from '@my/playwright/fixtures/send-accounts'
import { test as supawrightTest } from '@my/playwright/fixtures/supawright'
import { debug, Debugger } from 'debug'
import { supabaseAdmin } from 'app/utils/supabase/admin'
import { countries } from 'app/utils/country'

const randomCountry = () =>
  countries[Math.floor(Math.random() * countries.length)] as (typeof countries)[number]

const test = mergeTests(sendAccountTest, supawrightTest)

let log: Debugger
let otherUserId: string

test.beforeEach(async ({ page }) => {
  log = debug(`test:profile:${test.info().parallelIndex}`)
  const randomNumber = Math.floor(Math.random() * 1e9)
  const country = randomCountry()
  const { data, error } = await supabaseAdmin.auth.signUp({
    phone: `+${country.dialCode}${randomNumber}`,
    password: 'changeme',
  })
  if (error) {
    log('error creating user', error)
    throw error
  }
  if (!data?.user) {
    throw new Error('user not created')
  }
  if (!data?.session) {
    throw new Error('session not created')
  }
  log('created user', data)
  otherUserId = data.user.id
})

test.afterEach(async () => {
  const { parallelIndex } = test.info()
  await supabaseAdmin.auth.admin.deleteUser(otherUserId).then(({ error }) => {
    if (error) {
      log('error deleting user', `id=${parallelIndex}`, `user=${otherUserId}`, error)
      throw error
    }
  })
})

test('should work', async ({ page, supawright }) => {
  expect(otherUserId).toBeDefined()
  const result = await supawright.create('tags', {
    name: 'tag1',
    status: 'confirmed',
    user_id: otherUserId,
  })
  log('created tag1', result)
  expect(result).toBeDefined()
  await page.goto('/profile/tag1')
  const title = await page.title()
  expect(title).toBe('Send | Profile')
})
0xBigBoss commented 8 months ago

Is this best solved with overrides?

isaacharrisholt commented 8 months ago

Supawright will create auth users automatically, but if you want custom values in fields, then yes, overrides is the way to go!

Alternatively, there's a createUser method you can use to do it more manually.

0xBigBoss commented 8 months ago

Ahh superb. createUser worked like a charm. Supawright could not handle a default value for user_id = auth.uid() so it was always null. My guess is it's not required so it skips it, but auth.uid() will be null for the service role.

So my fix for now is just to use supawright.createUser and pass the user_id.

create table tags
(
      user_id    uuid                     default auth.uid()            not null references auth.users on delete cascade
);

Feel free to close the issue. Thx again!

isaacharrisholt commented 8 months ago

Ah, yeah. Supawright will ignore any non-required fields, including those with default values.

This could potentially be changed for auth.uid(), but as you pointed out, it's a service role, so that would be null.

Perhaps there's scope for allowing Supawright to impersonate a given user, but I think that's probably outside the scope of an integration testing harness.

Could definitely be useful for testing RLS policies, but there are much better alternatives for that, like the test helpers available on dbdev.