Skip to content

Commit

Permalink
fix(editor): Auto focus first fields on SignIn, SignUp and ForgotMyPa…
Browse files Browse the repository at this point in the history
…ssword views (#11445)
  • Loading branch information
cstuncsik authored Nov 1, 2024
1 parent 7d6dccb commit 5b5bd72
Show file tree
Hide file tree
Showing 6 changed files with 389 additions and 0 deletions.
137 changes: 137 additions & 0 deletions packages/editor-ui/src/views/ForgotMyPasswordView.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { createComponentRenderer } from '@/__tests__/render';
import { mockedStore } from '@/__tests__/utils';
import { createTestingPinia } from '@pinia/testing';
import userEvent from '@testing-library/user-event';
import ForgotMyPasswordView from '@/views/ForgotMyPasswordView.vue';
import { useToast } from '@/composables/useToast';
import { useUsersStore } from '@/stores/users.store';
import { useSettingsStore } from '@/stores/settings.store';

vi.mock('vue-router', () => {
const push = vi.fn();
const replace = vi.fn();
const query = {};
return {
useRouter: () => ({
push,
replace,
}),
useRoute: () => ({
query,
}),
RouterLink: {
template: '<a><slot /></a>',
},
};
});

vi.mock('@/composables/useToast', () => {
const showError = vi.fn();
const showMessage = vi.fn();
return {
useToast: () => ({
showError,
showMessage,
}),
};
});

const renderComponent = createComponentRenderer(ForgotMyPasswordView, {
global: {
stubs: {
'router-link': {
template: '<a href="#"><slot /></a>',
},
},
},
});

let toast: ReturnType<typeof useToast>;
let usersStore: ReturnType<typeof mockedStore<typeof useUsersStore>>;
let settingsStore: ReturnType<typeof mockedStore<typeof useSettingsStore>>;

describe('ForgotMyPasswordView', () => {
beforeEach(() => {
vi.clearAllMocks();

createTestingPinia();

toast = useToast();
usersStore = mockedStore(useUsersStore);
settingsStore = mockedStore(useSettingsStore);
});

it('should not throw error when opened', () => {
expect(() => renderComponent()).not.toThrow();
});

it('should show email sending setup warning', async () => {
const { getByRole, queryByRole } = renderComponent();

const link = getByRole('link');
const emailInput = queryByRole('textbox');

expect(emailInput).not.toBeInTheDocument();
expect(link).toBeVisible();
expect(link).toHaveTextContent('Back to sign in');
});

it('should show form and submit', async () => {
settingsStore.isSmtpSetup = true;
usersStore.sendForgotPasswordEmail.mockResolvedValueOnce();

const { getByRole } = renderComponent();

const link = getByRole('link');
const emailInput = getByRole('textbox');
const submitButton = getByRole('button');

expect(emailInput).toBeVisible();
expect(link).toBeVisible();
expect(link).toHaveTextContent('Back to sign in');

// TODO: Remove manual tabbing when the following issue is fixed (it should fail the test anyway)
// https://github.com/testing-library/vue-testing-library/issues/317
await userEvent.tab();
expect(document.activeElement).toBe(emailInput);

await userEvent.type(emailInput, '[email protected]');
await userEvent.click(submitButton);

expect(usersStore.sendForgotPasswordEmail).toHaveBeenCalledWith({
email: '[email protected]',
});

expect(toast.showMessage).toHaveBeenCalledWith(
expect.objectContaining({
title: expect.any(String),
message: expect.any(String),
}),
);
});

it('should show form and error toast when submit has error', async () => {
settingsStore.isSmtpSetup = true;
usersStore.sendForgotPasswordEmail.mockRejectedValueOnce({
httpStatusCode: 400,
});

const { getByRole } = renderComponent();

const emailInput = getByRole('textbox');
const submitButton = getByRole('button');

await userEvent.type(emailInput, '[email protected]');
await userEvent.click(submitButton);

expect(usersStore.sendForgotPasswordEmail).toHaveBeenCalledWith({
email: '[email protected]',
});

expect(toast.showMessage).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
}),
);
});
});
1 change: 1 addition & 0 deletions packages/editor-ui/src/views/ForgotMyPasswordView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const formConfig = computed(() => {
validationRules: [{ name: 'VALID_EMAIL' }],
autocomplete: 'email',
capitalize: true,
focusInitially: true,
},
},
];
Expand Down
100 changes: 100 additions & 0 deletions packages/editor-ui/src/views/SigninView.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { createComponentRenderer } from '@/__tests__/render';
import { mockedStore } from '@/__tests__/utils';
import { createTestingPinia } from '@pinia/testing';
import userEvent from '@testing-library/user-event';
import { useRouter } from 'vue-router';
import SigninView from '@/views/SigninView.vue';
import { useUsersStore } from '@/stores/users.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useTelemetry } from '@/composables/useTelemetry';

vi.mock('vue-router', () => {
const push = vi.fn();
return {
useRouter: () => ({
push,
}),
useRoute: () => ({
query: {
redirect: '/home/workflows',
},
}),
RouterLink: {
template: '<a><slot /></a>',
},
};
});

vi.mock('@/composables/useTelemetry', () => {
const track = vi.fn();
return {
useTelemetry: () => ({
track,
}),
};
});

const renderComponent = createComponentRenderer(SigninView);

let usersStore: ReturnType<typeof mockedStore<typeof useUsersStore>>;
let settingsStore: ReturnType<typeof mockedStore<typeof useSettingsStore>>;

let router: ReturnType<typeof useRouter>;
let telemetry: ReturnType<typeof useTelemetry>;

describe('SigninView', () => {
beforeEach(() => {
createTestingPinia();
usersStore = mockedStore(useUsersStore);
settingsStore = mockedStore(useSettingsStore);

router = useRouter();
telemetry = useTelemetry();
});

it('should not throw error when opened', () => {
expect(() => renderComponent()).not.toThrow();
});

it('should show and submit email/password form (happy path)', async () => {
settingsStore.isCloudDeployment = false;
usersStore.loginWithCreds.mockResolvedValueOnce();

const { getByRole, queryByTestId, container } = renderComponent();
const emailInput = container.querySelector('input[type="email"]');
const passwordInput = container.querySelector('input[type="password"]');
const submitButton = getByRole('button', { name: 'Sign in' });

if (!emailInput || !passwordInput) {
throw new Error('Inputs not found');
}

expect(queryByTestId('mfa-login-form')).not.toBeInTheDocument();

expect(emailInput).toBeVisible();
expect(passwordInput).toBeVisible();

// TODO: Remove manual tabbing when the following issue is fixed (it should fail the test anyway)
// https://github.com/testing-library/vue-testing-library/issues/317
await userEvent.tab();
expect(document.activeElement).toBe(emailInput);

await userEvent.type(emailInput, '[email protected]');
await userEvent.type(passwordInput, 'password');

await userEvent.click(submitButton);

expect(usersStore.loginWithCreds).toHaveBeenCalledWith({
email: '[email protected]',
password: 'password',
mfaToken: undefined,
mfaRecoveryCode: undefined,
});

expect(telemetry.track).toHaveBeenCalledWith('User attempted to login', {
result: 'success',
});

expect(router.push).toHaveBeenCalledWith('/home/workflows');
});
});
1 change: 1 addition & 0 deletions packages/editor-ui/src/views/SigninView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const formConfig: IFormBoxConfig = reactive({
validateOnBlur: false,
autocomplete: 'email',
capitalize: true,
focusInitially: true,
},
},
{
Expand Down
Loading

0 comments on commit 5b5bd72

Please sign in to comment.