Skip to content

Commit

Permalink
Merge branch 'vitest' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
caleb531 committed Nov 18, 2023
2 parents c1e4303 + 93e243f commit cc4bfd3
Show file tree
Hide file tree
Showing 41 changed files with 1,390 additions and 2,301 deletions.
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
VITE_NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=a1b2c3d4
GOTRUE_SECURITY_CAPTCHA_ENABLED="true"
GOTRUE_SECURITY_CAPTCHA_PROVIDER="turnstile"
Expand Down
4 changes: 3 additions & 1 deletion __tests__/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"extends": ["plugin:vitest-globals/recommended"],
"env": {
"jest/globals": true
"jest/globals": true,
"vitest-globals/env": true
}
}
78 changes: 44 additions & 34 deletions __tests__/AccountHeader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { POST as SignOutPOST } from '@app/auth/sign-out/route';
import Home from '@app/page';
import { getSession, getUser } from '@components/authUtils.client';
import '@testing-library/jest-dom';
import { act, screen, waitFor } from '@testing-library/react';
import { act, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import fetch from '@tests/__mocks__/fetchMock';
import { supabase } from '@tests/__mocks__/supabaseAuthHelpersMock';
import { renderServerComponent } from '@tests/__utils__/renderServerComponent';
import {
Expand All @@ -16,32 +17,41 @@ import {
mockLocationObject,
restoreLocationObject
} from '@tests/__utils__/testUtils';
import fetch from 'jest-fetch-mock';

describe('Account Header', () => {
beforeEach(() => {
mockLocationObject();
});

afterEach(() => {
jest.restoreAllMocks();
vi.restoreAllMocks();
restoreLocationObject();
});

it('should provide links to Sign Up / Sign In when not signed in', async () => {
await renderServerComponent(<Home />);
await userEvent.click(screen.getByRole('button', { name: 'Sign Up/In' }));
expect(screen.getByRole('link', { name: 'Sign Up' })).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Sign In' })).toBeInTheDocument();
await userEvent.click(
await screen.findByRole('button', { name: 'Sign Up/In' })
);
expect(
await screen.findByRole('link', { name: 'Sign Up' })
).toBeInTheDocument();
expect(
await screen.findByRole('link', { name: 'Sign In' })
).toBeInTheDocument();
});

it('should close Sign Up / Sign In modal', async () => {
await renderServerComponent(<Home />);
await userEvent.click(screen.getByRole('button', { name: 'Sign Up/In' }));
await userEvent.click(
await screen.findByRole('button', { name: 'Sign Up/In' })
);
expect(
screen.getByRole('heading', { name: 'Account' })
await screen.findByRole('heading', { name: 'Account' })
).toBeInTheDocument();
await userEvent.click(screen.getByRole('button', { name: 'Close Modal' }));
await userEvent.click(
await screen.findByRole('button', { name: 'Close Modal' })
);
expect(
screen.queryByRole('heading', { name: 'Account' })
).not.toBeInTheDocument();
Expand All @@ -54,23 +64,23 @@ describe('Account Header', () => {
return JSON.stringify({ success: true });
});
mockConfirmOnce(() => true);
jest.spyOn(supabase.auth, 'signOut').mockImplementation(() => {
vi.spyOn(supabase.auth, 'signOut').mockImplementation(() => {
return {
error: null
} as any;
});
await renderServerComponent(<Home />);
await waitFor(() => {
expect(
screen.getByRole('button', { name: 'Your Account' })
).toBeInTheDocument();
});
await userEvent.click(screen.getByRole('button', { name: 'Your Account' }));
expect(
await screen.findByRole('button', { name: 'Your Account' })
).toBeInTheDocument();
await userEvent.click(
await screen.findByRole('button', { name: 'Your Account' })
);
localStorage.setItem('faith-dashboard-whatever', 'true');
const log = jest.spyOn(console, 'log').mockImplementation(() => {
const log = vi.spyOn(console, 'log').mockImplementation(() => {
// noop
});
await userEvent.click(screen.getByText('Sign Out'));
await userEvent.click(await screen.findByText('Sign Out'));
log.mockReset();
expect(localStorage.getItem('faith-dashboard-whatever')).toEqual(null);
await act(async () => {
Expand All @@ -83,13 +93,13 @@ describe('Account Header', () => {
await mockSupabaseUser();
await mockSupabaseSession();
await renderServerComponent(<Home />);
await waitFor(() => {
expect(
screen.getByRole('button', { name: 'Your Account' })
).toBeInTheDocument();
});
await userEvent.click(screen.getByRole('button', { name: 'Your Account' }));
const overlay = screen.getByRole('button', { name: 'Close Menu' });
expect(
await screen.findByRole('button', { name: 'Your Account' })
).toBeInTheDocument();
await userEvent.click(
await screen.findByRole('button', { name: 'Your Account' })
);
const overlay = await screen.findByRole('button', { name: 'Close Menu' });
expect(overlay).toBeInTheDocument();
await userEvent.click(overlay);
expect(overlay).not.toBeInTheDocument();
Expand All @@ -99,20 +109,20 @@ describe('Account Header', () => {
await mockSupabaseUser();
await mockSupabaseSession();
mockConfirmOnce(() => false);
jest.spyOn(supabase.auth, 'signOut').mockImplementation(() => {
vi.spyOn(supabase.auth, 'signOut').mockImplementation(() => {
return {
error: null
} as any;
});
await renderServerComponent(<Home />);
await waitFor(() => {
expect(
screen.getByRole('button', { name: 'Your Account' })
).toBeInTheDocument();
});
await userEvent.click(screen.getByRole('button', { name: 'Your Account' }));
expect(
await screen.findByRole('button', { name: 'Your Account' })
).toBeInTheDocument();
await userEvent.click(
await screen.findByRole('button', { name: 'Your Account' })
);
localStorage.setItem('faith-dashboard-whatever', 'true');
await userEvent.click(screen.getByText('Sign Out'));
await userEvent.click(await screen.findByText('Sign Out'));
expect(localStorage.getItem('faith-dashboard-whatever')).toEqual('true');
await act(async () => {
await getUser();
Expand All @@ -121,7 +131,7 @@ describe('Account Header', () => {
});

it('should sign out on server side', async () => {
jest.spyOn(supabase.auth, 'signOut').mockImplementationOnce(async () => {
vi.spyOn(supabase.auth, 'signOut').mockImplementationOnce(async () => {
return { data: { user: {}, session: {} }, error: null } as any;
});
await callRouteHandler({
Expand Down
60 changes: 38 additions & 22 deletions __tests__/AccountSettings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { POST as UpdateUserNamePOST } from '@app/auth/update-user-name/route';
import '@testing-library/jest-dom';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import fetch from '@tests/__mocks__/fetchMock';
import { renderServerComponent } from '@tests/__utils__/renderServerComponent';
import fetch from 'jest-fetch-mock';
import { headers } from 'next/headers';
import { redirect } from 'next/navigation';
import { mockCaptchaSuccess } from './__mocks__/captchaMockUtils';
import { supabase } from './__mocks__/supabaseAuthHelpersMock';
import {
mockSupabaseSession,
Expand All @@ -29,7 +30,7 @@ describe('Account Settings page', () => {
});

afterEach(() => {
jest.restoreAllMocks();
vi.restoreAllMocks();
restoreLocationObject();
});

Expand All @@ -38,25 +39,27 @@ describe('Account Settings page', () => {
await mockSupabaseSession();
await renderServerComponent(<AccountSettings />);
expect(
screen.getByRole('heading', {
await screen.findByRole('heading', {
name: 'Account Settings | Faith Dashboard'
})
).toBeInTheDocument();
});

it('should redirect to Sign In page if signed out', async () => {
mockCaptchaSuccess('mytoken');
// Mock the value of the x-url header so that the source code can correctly
// determine the URL to redirect to (after sign-in)
jest
.spyOn(headers(), 'get')
.mockImplementation(() => 'https://localhost:3000/account');
vi.spyOn(headers(), 'get').mockImplementation(
() => 'https://localhost:3000/account'
);
await renderServerComponent(<AccountSettings />);
expect(redirect).toHaveBeenCalledWith(
`/sign-in?redirect_to=${encodeURIComponent('/account')}`
);
});

it('should update name of user successfully', async () => {
mockCaptchaSuccess('mytoken');
await mockSupabaseUser();
await mockSupabaseSession();
fetch.mockIf(/\/auth\/update-user-name/, async () => {
Expand All @@ -70,7 +73,9 @@ describe('Account Settings page', () => {
},
{ clearFieldsFirst: true }
);
await userEvent.click(screen.getByRole('button', { name: 'Save Details' }));
await userEvent.click(
await screen.findByRole('button', { name: 'Save Details' })
);
const [actualFetchUrl, actualFetchOptions] = fetch.mock.calls[0];
expect(actualFetchUrl).toEqual('/auth/update-user-name');
expect(actualFetchOptions?.method?.toUpperCase()).toEqual('POST');
Expand All @@ -85,6 +90,7 @@ describe('Account Settings page', () => {
});

it('should request email change successfully', async () => {
mockCaptchaSuccess('mytoken');
await mockSupabaseUser();
await mockSupabaseSession();
fetch.mockIf(/\/auth\/request-email-change/, async () => {
Expand All @@ -98,7 +104,9 @@ describe('Account Settings page', () => {
},
{ clearFieldsFirst: true }
);
await userEvent.click(screen.getByRole('button', { name: 'Change Email' }));
await userEvent.click(
await screen.findByRole('button', { name: 'Change Email' })
);
const [actualFetchUrl, actualFetchOptions] = fetch.mock.calls[0];
expect(actualFetchUrl).toEqual('/auth/request-email-change');
expect(actualFetchOptions?.method?.toUpperCase()).toEqual('POST');
Expand All @@ -113,14 +121,15 @@ describe('Account Settings page', () => {
});

it('should validate that emails are not matching', async () => {
mockCaptchaSuccess('mytoken');
await mockSupabaseUser();
await mockSupabaseSession();
await renderServerComponent(<AccountSettings />);
await typeIntoFormFields({
'New Email': '[email protected]',
'Confirm New Email': '[email protected]'
});
expect(screen.getByLabelText('Confirm New Email')).toHaveProperty(
expect(await screen.findByLabelText('Confirm New Email')).toHaveProperty(
'validationMessage',
'Emails must match'
);
Expand All @@ -131,16 +140,19 @@ describe('Account Settings page', () => {
await mockSupabaseSession();
await renderServerComponent(<AccountSettings />);
const requiredFields = ['New Email', 'Confirm New Email'];
await userEvent.click(screen.getByRole('button', { name: 'Change Email' }));
requiredFields.forEach((labelText) => {
expect(screen.getByLabelText(labelText)).toHaveProperty(
await userEvent.click(
await screen.findByRole('button', { name: 'Change Email' })
);
requiredFields.forEach(async (labelText) => {
expect(await screen.findByLabelText(labelText)).toHaveProperty(
'validity.valueMissing',
true
);
});
});

it('should cancel email change successfully', async () => {
mockCaptchaSuccess('mytoken');
await mockSupabaseUser({
email: '[email protected]',
new_email: '[email protected]',
Expand All @@ -152,7 +164,7 @@ describe('Account Settings page', () => {
});
await renderServerComponent(<AccountSettings />);
await userEvent.click(
screen.getByRole('button', { name: 'Cancel Email Change' })
await screen.findByRole('button', { name: 'Cancel Email Change' })
);
const [actualFetchUrl, actualFetchOptions] = fetch.mock.calls[0];
expect(actualFetchUrl).toEqual('/auth/cancel-email-change');
Expand All @@ -163,6 +175,7 @@ describe('Account Settings page', () => {
});

it('should change password successfully', async () => {
mockCaptchaSuccess('mytoken');
await mockSupabaseUser();
await mockSupabaseSession();
fetch.mockIf(/\/auth\/change-password/, async () => {
Expand All @@ -175,7 +188,7 @@ describe('Account Settings page', () => {
'Confirm New Password': 'CorrectHorseBatteryStaple'
});
await userEvent.click(
screen.getByRole('button', { name: 'Change Password' })
await screen.findByRole('button', { name: 'Change Password' })
);
const [actualFetchUrl, actualFetchOptions] = fetch.mock.calls[0];
expect(actualFetchUrl).toEqual('/auth/change-password');
Expand All @@ -189,6 +202,7 @@ describe('Account Settings page', () => {
});

it('should validate that passwords are not matching', async () => {
mockCaptchaSuccess('mytoken');
await mockSupabaseUser();
await mockSupabaseSession();
await renderServerComponent(<AccountSettings />);
Expand All @@ -197,13 +211,14 @@ describe('Account Settings page', () => {
'New Password': 'CorrectHorseBatteryStaple',
'Confirm New Password': 'CorrectHorseBatteryStale'
});
expect(screen.getByLabelText('Confirm New Password')).toHaveProperty(
expect(await screen.findByLabelText('Confirm New Password')).toHaveProperty(
'validationMessage',
'Passwords must match'
);
});

it('should require all Change Password fields to be populated', async () => {
mockCaptchaSuccess('mytoken');
await mockSupabaseUser();
await mockSupabaseSession();
await renderServerComponent(<AccountSettings />);
Expand All @@ -213,18 +228,19 @@ describe('Account Settings page', () => {
'Confirm New Password'
];
await userEvent.click(
screen.getByRole('button', { name: 'Change Password' })
await screen.findByRole('button', { name: 'Change Password' })
);
requiredFields.forEach((labelText) => {
expect(screen.getByLabelText(labelText)).toHaveProperty(
requiredFields.forEach(async (labelText) => {
expect(await screen.findByLabelText(labelText)).toHaveProperty(
'validity.valueMissing',
true
);
});
});

it('should change user name on server side', async () => {
jest.spyOn(supabase.auth, 'updateUser').mockImplementationOnce(async () => {
mockCaptchaSuccess('mytoken');
vi.spyOn(supabase.auth, 'updateUser').mockImplementationOnce(async () => {
return { data: {}, error: null } as any;
});
const fields = {
Expand All @@ -245,7 +261,7 @@ describe('Account Settings page', () => {
});

it('should change password on server side', async () => {
jest.spyOn(supabase, 'rpc').mockImplementationOnce(() => {
vi.spyOn(supabase, 'rpc').mockImplementationOnce(() => {
return { data: {}, error: null } as any;
});
const fields = {
Expand All @@ -265,7 +281,7 @@ describe('Account Settings page', () => {
});

it('should request email change on server side', async () => {
jest.spyOn(supabase.auth, 'updateUser').mockImplementationOnce(() => {
vi.spyOn(supabase.auth, 'updateUser').mockImplementationOnce(() => {
return { data: {}, error: null } as any;
});
const fields = {
Expand All @@ -283,7 +299,7 @@ describe('Account Settings page', () => {
});

it('should cancel email change on server side', async () => {
jest.spyOn(supabase, 'rpc').mockImplementationOnce(() => {
vi.spyOn(supabase, 'rpc').mockImplementationOnce(() => {
return { data: {}, error: null } as any;
});
await callRouteHandler({
Expand Down
Loading

0 comments on commit cc4bfd3

Please sign in to comment.