diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 28c34d2cd88..a3eb5a03fa6 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-14.19.0
+14.20.0
diff --git a/app/assets/javascripts/google_cloud/aiml/panel.vue b/app/assets/javascripts/google_cloud/aiml/panel.vue
new file mode 100644
index 00000000000..f591c47ac40
--- /dev/null
+++ b/app/assets/javascripts/google_cloud/aiml/panel.vue
@@ -0,0 +1,63 @@
+
+
+
+
${updatedText}
\n`); + expect(findNoteBody().props().note.note_html).toBe(`${updatedText}
\n`); - noteBody.vm.$emit('cancelForm', {}); + findNoteBody().vm.$emit('cancelForm', {}); await nextTick(); - noteBodyProps = noteBody.props(); - - expect(noteBodyProps.note.note_html).toBe(note.note_html); + expect(findNoteBody().props().note.note_html).toBe(note.note_html); }); }); @@ -371,7 +364,7 @@ describe('issue_note', () => { it('responds to handleFormUpdate', () => { createWrapper(); updateActions(); - wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', params); + findNoteBody().vm.$emit('handleFormUpdate', params); expect(wrapper.emitted('handleUpdateNote')).toHaveLength(1); }); @@ -380,16 +373,14 @@ describe('issue_note', () => { createWrapper(); updateActions(); - wrapper - .findComponent(NoteBody) - .vm.$emit('handleFormUpdate', { ...params, noteText: sensitiveMessage }); + findNoteBody().vm.$emit('handleFormUpdate', { ...params, noteText: sensitiveMessage }); expect(updateNote).not.toHaveBeenCalled(); }); it('does not stringify empty position', () => { createWrapper(); updateActions(); - wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', params); + findNoteBody().vm.$emit('handleFormUpdate', params); expect(updateNote.mock.calls[0][1].note.note.position).toBeUndefined(); }); @@ -398,7 +389,7 @@ describe('issue_note', () => { const expectation = JSON.stringify(position); createWrapper({ note: { ...note, position } }); updateActions(); - wrapper.findComponent(NoteBody).vm.$emit('handleFormUpdate', params); + findNoteBody().vm.$emit('handleFormUpdate', params); expect(updateNote.mock.calls[0][1].note.note.position).toBe(expectation); }); }); @@ -423,7 +414,7 @@ describe('issue_note', () => { createWrapper({ note: noteDef, discussionFile: null }, storeUpdater); - expect(wrapper.vm.diffFile).toBe(null); + expect(findNoteBody().props().file).toBe(null); }, ); @@ -441,7 +432,7 @@ describe('issue_note', () => { }, ); - expect(wrapper.vm.diffFile.testId).toBe('diffFileTest'); + expect(findNoteBody().props().file.testId).toBe('diffFileTest'); }); it('returns the provided diff file if the more robust getters fail', () => { @@ -457,7 +448,7 @@ describe('issue_note', () => { }, ); - expect(wrapper.vm.diffFile.testId).toBe('diffFileTest'); + expect(findNoteBody().props().file.testId).toBe('diffFileTest'); }); }); }); diff --git a/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js b/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js index 930d2fc8cfe..2a4037d76b7 100644 --- a/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js +++ b/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js @@ -58,7 +58,6 @@ describe('Diff Stats Dropdown', () => { const findChangedFiles = () => findChanged().findAllComponents(GlDropdownItem); const findNoFilesText = () => findChanged().findComponent(GlDropdownText); const findCollapsed = () => wrapper.findByTestId('diff-stats-additions-deletions-expanded'); - const findExpanded = () => wrapper.findByTestId('diff-stats-additions-deletions-collapsed'); const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); describe('file item', () => { @@ -88,24 +87,17 @@ describe('Diff Stats Dropdown', () => { }); describe.each` - changed | added | deleted | expectedDropdownHeader | expectedAddedDeletedExpanded | expectedAddedDeletedCollapsed - ${0} | ${0} | ${0} | ${'0 changed files'} | ${'+0 -0'} | ${'with 0 additions and 0 deletions'} - ${2} | ${0} | ${2} | ${'2 changed files'} | ${'+0 -2'} | ${'with 0 additions and 2 deletions'} - ${2} | ${2} | ${0} | ${'2 changed files'} | ${'+2 -0'} | ${'with 2 additions and 0 deletions'} - ${2} | ${1} | ${1} | ${'2 changed files'} | ${'+1 -1'} | ${'with 1 addition and 1 deletion'} - ${1} | ${0} | ${1} | ${'1 changed file'} | ${'+0 -1'} | ${'with 0 additions and 1 deletion'} - ${1} | ${1} | ${0} | ${'1 changed file'} | ${'+1 -0'} | ${'with 1 addition and 0 deletions'} - ${4} | ${2} | ${2} | ${'4 changed files'} | ${'+2 -2'} | ${'with 2 additions and 2 deletions'} + changed | added | deleted | expectedDropdownHeader | expectedAddedDeletedCollapsed + ${0} | ${0} | ${0} | ${'0 changed files'} | ${'with 0 additions and 0 deletions'} + ${2} | ${0} | ${2} | ${'2 changed files'} | ${'with 0 additions and 2 deletions'} + ${2} | ${2} | ${0} | ${'2 changed files'} | ${'with 2 additions and 0 deletions'} + ${2} | ${1} | ${1} | ${'2 changed files'} | ${'with 1 addition and 1 deletion'} + ${1} | ${0} | ${1} | ${'1 changed file'} | ${'with 0 additions and 1 deletion'} + ${1} | ${1} | ${0} | ${'1 changed file'} | ${'with 1 addition and 0 deletions'} + ${4} | ${2} | ${2} | ${'4 changed files'} | ${'with 2 additions and 2 deletions'} `( 'when there are $changed changed file(s), $added added and $deleted deleted file(s)', - ({ - changed, - added, - deleted, - expectedDropdownHeader, - expectedAddedDeletedExpanded, - expectedAddedDeletedCollapsed, - }) => { + ({ changed, added, deleted, expectedDropdownHeader, expectedAddedDeletedCollapsed }) => { beforeEach(() => { createComponent({ changed, added, deleted }); }); @@ -114,10 +106,6 @@ describe('Diff Stats Dropdown', () => { expect(findChanged().props('text')).toBe(expectedDropdownHeader); }); - it(`added and deleted count in expanded section should be '${expectedAddedDeletedExpanded}'`, () => { - expect(findExpanded().text()).toBe(expectedAddedDeletedExpanded); - }); - it(`added and deleted count in collapsed section should be '${expectedAddedDeletedCollapsed}'`, () => { expect(findCollapsed().text()).toBe(expectedAddedDeletedCollapsed); }); diff --git a/spec/frontend/vue_shared/components/projects_list/projects_list_item_spec.js b/spec/frontend/vue_shared/components/projects_list/projects_list_item_spec.js index dc67097d763..3e4d5c558f6 100644 --- a/spec/frontend/vue_shared/components/projects_list/projects_list_item_spec.js +++ b/spec/frontend/vue_shared/components/projects_list/projects_list_item_spec.js @@ -12,6 +12,7 @@ import { import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue'; import { ACCESS_LEVEL_LABELS } from '~/access_level/constants'; import { FEATURABLE_DISABLED, FEATURABLE_ENABLED } from '~/featurable/constants'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; jest.mock('lodash/uniqueId', () => (prefix) => `${prefix}1`); @@ -36,6 +37,7 @@ describe('ProjectsListItem', () => { const findForksLink = () => wrapper.findByRole('link', { name: ProjectsListItem.i18n.forks }); const findProjectTopics = () => wrapper.findByTestId('project-topics'); const findPopover = () => findProjectTopics().findComponent(GlPopover); + const findProjectDescription = () => wrapper.findByTestId('project-description'); it('renders project avatar', () => { createComponent(); @@ -105,6 +107,12 @@ describe('ProjectsListItem', () => { expect(starsLink.findComponent(GlIcon).props('name')).toBe('star-o'); }); + it('renders updated at', () => { + createComponent(); + + expect(wrapper.findComponent(TimeAgoTooltip).props('time')).toBe(project.updatedAt); + }); + describe('when issues are enabled', () => { it('renders issues count', () => { createComponent(); @@ -230,4 +238,29 @@ describe('ProjectsListItem', () => { }); }); }); + + describe('when project has a description', () => { + it('renders description', () => { + const descriptionHtml = 'Foo bar
'; + + createComponent({ + propsData: { + project: { + ...project, + descriptionHtml, + }, + }, + }); + + expect(findProjectDescription().element.innerHTML).toBe(descriptionHtml); + }); + }); + + describe('when project does not have a description', () => { + it('does not render description', () => { + createComponent(); + + expect(findProjectDescription().exists()).toBe(false); + }); + }); }); diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 17a6eec2a8e..349101a092f 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1181,30 +1181,6 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :projects d end end end - - context 'when the feature flag `rate_limit_for_unauthenticated_projects_api_access` is disabled' do - before do - stub_feature_flags(rate_limit_for_unauthenticated_projects_api_access: false) - end - - context 'when the user is not signed in' do - let_it_be(:current_user) { nil } - - it_behaves_like 'does not log request and does not block the request' do - def request - get api(path, current_user) - end - end - end - - context 'when the user is signed in' do - it_behaves_like 'does not log request and does not block the request' do - def request - get api(path, current_user) - end - end - end - end end end diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index 4b89403359f..0de1300bc50 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -38,7 +38,7 @@ JS_CONSOLE_FILTER = Regexp.union( CAPYBARA_WINDOW_SIZE = [1366, 768].freeze -SCREENSHOT_FILENAME_LENGTH = ENV['CI'] || ENV['CI_SERVER'] ? 255 : 99 +SCREENSHOT_FILENAME_LENGTH = ENV['CI'] || ENV['CI_SERVER'] ? 150 : 99 @blackhole_tcp_server = nil diff --git a/spec/support/helpers/keyset_pagination_helpers.rb b/spec/support/helpers/keyset_pagination_helpers.rb index 4bc20098e8c..4a476c47fda 100644 --- a/spec/support/helpers/keyset_pagination_helpers.rb +++ b/spec/support/helpers/keyset_pagination_helpers.rb @@ -7,14 +7,17 @@ module KeysetPaginationHelpers link.split(',').filter_map do |link| match = link.match(/<(?