-
Notifications
You must be signed in to change notification settings - Fork 296
[Remove Vuetify from Studio] 'Create an account' page #5701
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
base: unstable
Are you sure you want to change the base?
Changes from all commits
d601682
e296099
5cbe5cc
03f71fb
8798e70
79bd634
9025e23
8d5a99f
0c4bdf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| <template> | ||
|
|
||
| <KTextbox | ||
| :value="value" | ||
| type="email" | ||
| :label="label || $tr('emailLabel')" | ||
| :maxlength="maxlength" | ||
| :disabled="disabled" | ||
| :invalid="hasError" | ||
| :invalidText="errorText" | ||
| :showInvalidText="hasError" | ||
| v-bind="$attrs" | ||
| @input="handleInput" | ||
| @blur="$emit('blur')" | ||
| /> | ||
|
|
||
| </template> | ||
|
|
||
|
|
||
| <script> | ||
| export default { | ||
| name: 'StudioEmailField', | ||
| props: { | ||
| value: { | ||
| type: String, | ||
| default: '', | ||
| }, | ||
| label: { | ||
| type: String, | ||
| default: null, | ||
| }, | ||
| disabled: { | ||
| type: Boolean, | ||
| default: false, | ||
| }, | ||
| errorMessages: { | ||
| type: Array, | ||
| default: () => [], | ||
| }, | ||
| maxlength: { | ||
| type: [String, Number], | ||
| default: null, | ||
| }, | ||
| }, | ||
| computed: { | ||
| hasError() { | ||
| return this.errorMessages && this.errorMessages.length > 0; | ||
| }, | ||
| errorText() { | ||
| return this.hasError ? this.errorMessages[0] : ''; | ||
| }, | ||
| }, | ||
| methods: { | ||
| handleInput(value) { | ||
| this.$emit('input', value.trim()); | ||
| }, | ||
| }, | ||
| $trs: { | ||
| emailLabel: 'Email address', | ||
| }, | ||
| }; | ||
| </script> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| <template> | ||
|
|
||
| <KTextbox | ||
| :value="value" | ||
| type="password" | ||
| :label="label || $tr('passwordLabel')" | ||
| :invalid="hasError" | ||
| :invalidText="errorText" | ||
| :showInvalidText="hasError" | ||
| @input="$emit('input', $event)" | ||
| @blur="$emit('blur')" | ||
| /> | ||
|
|
||
| </template> | ||
|
|
||
|
|
||
| <script> | ||
|
|
||
| export default { | ||
| name: 'StudioPasswordField', | ||
| props: { | ||
| value: { | ||
| type: String, | ||
| default: '', | ||
| }, | ||
| label: { | ||
| type: String, | ||
| default: null, | ||
| }, | ||
| errorMessages: { | ||
| type: Array, | ||
| default: () => [], | ||
| }, | ||
| }, | ||
| computed: { | ||
| hasError() { | ||
| return this.errorMessages && this.errorMessages.length > 0; | ||
| }, | ||
| errorText() { | ||
| return this.hasError ? this.errorMessages[0] : ''; | ||
| }, | ||
| }, | ||
| $trs: { | ||
| passwordLabel: 'Password', | ||
| }, | ||
| }; | ||
|
|
||
| </script> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import VueRouter from 'vue-router'; | ||
| import { render, screen, fireEvent } from '@testing-library/vue'; | ||
| import StudioEmailField from '../StudioEmailField.vue'; | ||
|
|
||
| const renderComponent = (props = {}) => | ||
| render(StudioEmailField, { | ||
| router: new VueRouter(), | ||
| props: { | ||
| value: '', | ||
| ...props, | ||
| }, | ||
| }); | ||
|
|
||
| describe('StudioEmailField', () => { | ||
| describe('rendering', () => { | ||
| it('renders with the default "Email address" label', () => { | ||
| renderComponent(); | ||
| expect(screen.getByLabelText(/email address/i)).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('renders with a custom label when provided', () => { | ||
| renderComponent({ label: 'Work email' }); | ||
| expect(screen.getByLabelText(/work email/i)).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('is disabled when the disabled prop is true', () => { | ||
| renderComponent({ disabled: true }); | ||
| expect(screen.getByLabelText(/email address/i)).toBeDisabled(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('input handling', () => { | ||
| it('emits trimmed value — strips leading and trailing whitespace', async () => { | ||
| const { emitted } = renderComponent(); | ||
| const input = screen.getByLabelText(/email address/i); | ||
| await fireEvent.update(input, ' test@example.com '); | ||
| expect(emitted().input).toBeTruthy(); | ||
| expect(emitted().input[0][0]).toBe('test@example.com'); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. praise: Good test coverage — rendering, input handling (trimming), blur events, and error display are all covered using |
||
| }); | ||
|
|
||
| it('emits blur event when the field loses focus', async () => { | ||
| const { emitted } = renderComponent(); | ||
| const input = screen.getByLabelText(/email address/i); | ||
| await fireEvent.blur(input); | ||
| expect(emitted().blur).toBeTruthy(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('error display', () => { | ||
| it('shows the first error message when errorMessages is non-empty', () => { | ||
| renderComponent({ errorMessages: ['Please enter a valid email address'] }); | ||
| expect(screen.getByText('Please enter a valid email address')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('shows no error text when errorMessages is empty', () => { | ||
| renderComponent({ errorMessages: [] }); | ||
| expect(screen.queryByText('Please enter a valid email address')).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('shows only the first error when multiple messages are provided', () => { | ||
| renderComponent({ errorMessages: ['First error', 'Second error'] }); | ||
| expect(screen.getByText('First error')).toBeVisible(); | ||
| expect(screen.queryByText('Second error')).not.toBeInTheDocument(); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import VueRouter from 'vue-router'; | ||
| import { render, screen, fireEvent } from '@testing-library/vue'; | ||
| import StudioPasswordField from '../StudioPasswordField.vue'; | ||
|
|
||
| const renderComponent = (props = {}) => | ||
| render(StudioPasswordField, { | ||
| router: new VueRouter(), | ||
| props: { | ||
| value: '', | ||
| ...props, | ||
| }, | ||
| }); | ||
|
|
||
| describe('StudioPasswordField', () => { | ||
| describe('rendering', () => { | ||
| it('renders with the default "Password" label', () => { | ||
| renderComponent(); | ||
| expect(screen.getByLabelText(/^password$/i)).toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('renders with a custom label when provided', () => { | ||
| renderComponent({ label: 'Confirm password' }); | ||
| expect(screen.getByLabelText(/confirm password/i)).toBeInTheDocument(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('input handling', () => { | ||
| it('emits raw value without trimming whitespace', async () => { | ||
| const { emitted } = renderComponent(); | ||
| const input = screen.getByLabelText(/^password$/i); | ||
| await fireEvent.update(input, ' mypassword '); | ||
| expect(emitted().input).toBeTruthy(); | ||
| expect(emitted().input[0][0]).toBe(' mypassword '); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @LianaHarris360 unless you already know, would you please be able to double-check on whether not trimming password is indeed expected and how it plays with backend? I would just like to be sure we won't cause any issues with passwords.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (also, the intention is use |
||
| }); | ||
|
|
||
| it('emits blur event when the field loses focus', async () => { | ||
| const { emitted } = renderComponent(); | ||
| const input = screen.getByLabelText(/^password$/i); | ||
| await fireEvent.blur(input); | ||
| expect(emitted().blur).toBeTruthy(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('error display', () => { | ||
| it('shows the first error message when errorMessages is non-empty', () => { | ||
| renderComponent({ errorMessages: ['Password should be at least 8 characters long'] }); | ||
| expect(screen.getByText('Password should be at least 8 characters long')).toBeVisible(); | ||
| }); | ||
|
|
||
| it('shows no error text when errorMessages is empty', () => { | ||
| renderComponent({ errorMessages: [] }); | ||
| expect( | ||
| screen.queryByText('Password should be at least 8 characters long'), | ||
| ).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('shows only the first error when multiple messages are provided', () => { | ||
| renderComponent({ errorMessages: ['First error', 'Second error'] }); | ||
| expect(screen.getByText('First error')).toBeVisible(); | ||
| expect(screen.queryByText('Second error')).not.toBeInTheDocument(); | ||
| }); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.