diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-10 21:09:24 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-10 21:09:24 +0000 |
commit | 02c3b2af448be6a5004e8d833cbcbf8e5f185210 (patch) | |
tree | 27359dc5c21a8901c9eb95a0101cb97087b1f4ac /spec | |
parent | 577bb49691b11bc8ebae3a4966153ed39af60d87 (diff) | |
download | gitlab-ce-02c3b2af448be6a5004e8d833cbcbf8e5f185210.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r-- | spec/frontend/pipelines/test_reports/stores/getters_spec.js | 9 | ||||
-rw-r--r-- | spec/frontend/pipelines/test_reports/stores/utils_spec.js | 16 | ||||
-rw-r--r-- | spec/frontend/pipelines/test_reports/test_suite_table_spec.js | 10 | ||||
-rw-r--r-- | spec/frontend/search_settings/components/search_settings_spec.js | 100 | ||||
-rw-r--r-- | spec/frontend/search_settings/mount_spec.js | 5 | ||||
-rw-r--r-- | spec/frontend/settings_panels_spec.js | 10 | ||||
-rw-r--r-- | spec/lib/gitlab/database/migrations/instrumentation_spec.rb | 76 | ||||
-rw-r--r-- | spec/tasks/gitlab/db_rake_spec.rb | 51 | ||||
-rw-r--r-- | spec/views/layouts/_head.html.haml_spec.rb | 42 |
9 files changed, 246 insertions, 73 deletions
diff --git a/spec/frontend/pipelines/test_reports/stores/getters_spec.js b/spec/frontend/pipelines/test_reports/stores/getters_spec.js index 7382a6beefa..aa541b6bca5 100644 --- a/spec/frontend/pipelines/test_reports/stores/getters_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/getters_spec.js @@ -1,6 +1,10 @@ import { getJSONFixture } from 'helpers/fixtures'; import * as getters from '~/pipelines/stores/test_reports/getters'; -import { iconForTestStatus, formattedTime } from '~/pipelines/stores/test_reports/utils'; +import { + iconForTestStatus, + formatFilePath, + formattedTime, +} from '~/pipelines/stores/test_reports/utils'; describe('Getters TestReports Store', () => { let state; @@ -8,6 +12,7 @@ describe('Getters TestReports Store', () => { const testReports = getJSONFixture('pipelines/test_report.json'); const defaultState = { + blobPath: '/test/blob/path', testReports, selectedSuiteIndex: 0, pageInfo: { @@ -17,6 +22,7 @@ describe('Getters TestReports Store', () => { }; const emptyState = { + blobPath: '', testReports: {}, selectedSuite: null, pageInfo: { @@ -74,6 +80,7 @@ describe('Getters TestReports Store', () => { const expected = testReports.test_suites[0].test_cases .map((x) => ({ ...x, + filePath: `${state.blobPath}/${formatFilePath(x.file)}`, formattedTime: formattedTime(x.execution_time), icon: iconForTestStatus(x.status), })) diff --git a/spec/frontend/pipelines/test_reports/stores/utils_spec.js b/spec/frontend/pipelines/test_reports/stores/utils_spec.js index 7e632d099fc..703fe69026c 100644 --- a/spec/frontend/pipelines/test_reports/stores/utils_spec.js +++ b/spec/frontend/pipelines/test_reports/stores/utils_spec.js @@ -1,6 +1,20 @@ -import { formattedTime } from '~/pipelines/stores/test_reports/utils'; +import { formatFilePath, formattedTime } from '~/pipelines/stores/test_reports/utils'; describe('Test reports utils', () => { + describe('formatFilePath', () => { + it.each` + file | expected + ${'./test.js'} | ${'test.js'} + ${'/test.js'} | ${'test.js'} + ${'.//////////////test.js'} | ${'test.js'} + ${'test.js'} | ${'test.js'} + ${'mock/path./test.js'} | ${'mock/path./test.js'} + ${'./mock/path./test.js'} | ${'mock/path./test.js'} + `('should format $file to be $expected', ({ file, expected }) => { + expect(formatFilePath(file)).toBe(expected); + }); + }); + describe('formattedTime', () => { describe('when time is smaller than a second', () => { it('should return time in milliseconds fixed to 2 decimals', () => { diff --git a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js index 57545aec50f..6aab4f28774 100644 --- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js +++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js @@ -1,9 +1,10 @@ import Vuex from 'vuex'; import { shallowMount, createLocalVue } from '@vue/test-utils'; -import { GlButton, GlFriendlyWrap, GlPagination } from '@gitlab/ui'; +import { GlButton, GlFriendlyWrap, GlLink, GlPagination } from '@gitlab/ui'; import { getJSONFixture } from 'helpers/fixtures'; import SuiteTable from '~/pipelines/components/test_reports/test_suite_table.vue'; import * as getters from '~/pipelines/stores/test_reports/getters'; +import { formatFilePath } from '~/pipelines/stores/test_reports/utils'; import { TestStatus } from '~/pipelines/constants'; import skippedTestCases from './mock_data'; @@ -20,15 +21,18 @@ describe('Test reports suite table', () => { testSuite.test_cases = [...testSuite.test_cases, ...skippedTestCases]; const testCases = testSuite.test_cases; + const blobPath = '/test/blob/path'; const noCasesMessage = () => wrapper.find('.js-no-test-cases'); const allCaseRows = () => wrapper.findAll('.js-case-row'); const findCaseRowAtIndex = (index) => wrapper.findAll('.js-case-row').at(index); + const findLinkForRow = (row) => row.find(GlLink); const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`); const createComponent = (suite = testSuite, perPage = 20) => { store = new Vuex.Store({ state: { + blobPath, testReports: { test_suites: [suite], }, @@ -82,9 +86,13 @@ describe('Test reports suite table', () => { it('renders the file name for the test with a copy button', () => { const { file } = testCases[0]; + const relativeFile = formatFilePath(file); + const filePath = `${blobPath}/${relativeFile}`; const row = findCaseRowAtIndex(0); + const fileLink = findLinkForRow(row); const button = row.find(GlButton); + expect(fileLink.attributes('href')).toBe(filePath); expect(row.text()).toContain(file); expect(button.exists()).toBe(true); expect(button.attributes('data-clipboard-text')).toBe(file); diff --git a/spec/frontend/search_settings/components/search_settings_spec.js b/spec/frontend/search_settings/components/search_settings_spec.js index b80f9b15abf..173936e1ce3 100644 --- a/spec/frontend/search_settings/components/search_settings_spec.js +++ b/spec/frontend/search_settings/components/search_settings_spec.js @@ -2,6 +2,7 @@ import { GlSearchBoxByType } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import SearchSettings from '~/search_settings/components/search_settings.vue'; import { HIGHLIGHT_CLASS, HIDE_CLASS } from '~/search_settings/constants'; +import { isExpanded, expandSection, closeSection } from '~/settings_panels'; describe('search_settings/components/search_settings.vue', () => { const ROOT_ID = 'content-body'; @@ -9,6 +10,8 @@ describe('search_settings/components/search_settings.vue', () => { const SEARCH_TERM = 'Delete project'; const GENERAL_SETTINGS_ID = 'js-general-settings'; const ADVANCED_SETTINGS_ID = 'js-advanced-settings'; + const EXTRA_SETTINGS_ID = 'js-extra-settings'; + let wrapper; const buildWrapper = () => { @@ -16,10 +19,15 @@ describe('search_settings/components/search_settings.vue', () => { propsData: { searchRoot: document.querySelector(`#${ROOT_ID}`), sectionSelector: SECTION_SELECTOR, + isExpandedFn: isExpanded, + }, + // Add real listeners so we can simplify and strengthen some tests. + listeners: { + expand: expandSection, + collapse: closeSection, }, }); }; - const sections = () => Array.from(document.querySelectorAll(SECTION_SELECTOR)); const sectionsCount = () => sections().length; const visibleSectionsCount = () => @@ -39,7 +47,10 @@ describe('search_settings/components/search_settings.vue', () => { <section id="${GENERAL_SETTINGS_ID}" class="settings"> <span>General</span> </section> - <section id="${ADVANCED_SETTINGS_ID}" class="settings"> + <section id="${ADVANCED_SETTINGS_ID}" class="settings expanded"> + <span>Advanced</span> + </section> + <section id="${EXTRA_SETTINGS_ID}" class="settings"> <span>${SEARCH_TERM}</span> </section> </div> @@ -52,17 +63,6 @@ describe('search_settings/components/search_settings.vue', () => { wrapper.destroy(); }); - it('expands first section and collapses the rest', () => { - clearSearch(); - - const [firstSection, ...otherSections] = sections(); - - expect(wrapper.emitted()).toEqual({ - expand: [[firstSection]], - collapse: otherSections.map((x) => [x]), - }); - }); - it('hides sections that do not match the search term', () => { const hiddenSection = document.querySelector(`#${GENERAL_SETTINGS_ID}`); search(SEARCH_TERM); @@ -72,12 +72,11 @@ describe('search_settings/components/search_settings.vue', () => { }); it('expands section that matches the search term', () => { - const section = document.querySelector(`#${ADVANCED_SETTINGS_ID}`); + const section = document.querySelector(`#${EXTRA_SETTINGS_ID}`); search(SEARCH_TERM); - // Last called because expand is always called once to reset the page state - expect(wrapper.emitted().expand[1][0]).toBe(section); + expect(wrapper.emitted('expand')).toEqual([[section]]); }); it('highlight elements that match the search term', () => { @@ -86,21 +85,64 @@ describe('search_settings/components/search_settings.vue', () => { expect(highlightedElementsCount()).toBe(1); }); - describe('when search term is cleared', () => { - beforeEach(() => { - search(SEARCH_TERM); - }); - - it('displays all sections', () => { - expect(visibleSectionsCount()).toBe(1); - clearSearch(); - expect(visibleSectionsCount()).toBe(sectionsCount()); + describe('default', () => { + it('test setup starts with expansion state', () => { + expect(sections().map(isExpanded)).toEqual([false, true, false]); }); - it('removes the highlight from all elements', () => { - expect(highlightedElementsCount()).toBe(1); - clearSearch(); - expect(highlightedElementsCount()).toBe(0); + describe('when searched and cleared', () => { + beforeEach(() => { + search('Test'); + clearSearch(); + }); + + it('displays all sections', () => { + expect(visibleSectionsCount()).toBe(sectionsCount()); + }); + + it('removes the highlight from all elements', () => { + expect(highlightedElementsCount()).toBe(0); + }); + + it('should preserve original expansion state', () => { + expect(sections().map(isExpanded)).toEqual([false, true, false]); + }); + + it('should preserve state by emitting events', () => { + const [first, mid, last] = sections(); + + expect(wrapper.emitted()).toEqual({ + expand: [[mid]], + collapse: [[first], [last]], + }); + }); + + describe('after multiple searches and clear', () => { + beforeEach(() => { + search('Test'); + search(SEARCH_TERM); + clearSearch(); + }); + + it('should preserve last expansion state', () => { + expect(sections().map(isExpanded)).toEqual([false, true, false]); + }); + }); + + describe('after user expands and collapses, search, and clear', () => { + beforeEach(() => { + const [first, mid] = sections(); + closeSection(mid); + expandSection(first); + + search(SEARCH_TERM); + clearSearch(); + }); + + it('should preserve last expansion state', () => { + expect(sections().map(isExpanded)).toEqual([true, false, false]); + }); + }); }); }); }); diff --git a/spec/frontend/search_settings/mount_spec.js b/spec/frontend/search_settings/mount_spec.js index b35266e56ae..8c141c4704e 100644 --- a/spec/frontend/search_settings/mount_spec.js +++ b/spec/frontend/search_settings/mount_spec.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import { setHTMLFixture } from 'helpers/fixtures'; import mount from '~/search_settings/mount'; import { expandSection, closeSection } from '~/settings_panels'; @@ -24,13 +23,13 @@ describe('search_settings/mount', () => { const section = { name: 'section' }; app.$refs.searchSettings.$emit('expand', section); - expect(expandSection).toHaveBeenCalledWith($(section)); + expect(expandSection).toHaveBeenCalledWith(section); }); it('calls settings_panel.closeSection when collapse event is emitted', () => { const section = { name: 'section' }; app.$refs.searchSettings.$emit('collapse', section); - expect(closeSection).toHaveBeenCalledWith($(section)); + expect(closeSection).toHaveBeenCalledWith(section); }); }); diff --git a/spec/frontend/settings_panels_spec.js b/spec/frontend/settings_panels_spec.js index 2c5d91a45bc..8666106d3c6 100644 --- a/spec/frontend/settings_panels_spec.js +++ b/spec/frontend/settings_panels_spec.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import initSettingsPanels from '~/settings_panels'; +import initSettingsPanels, { isExpanded } from '~/settings_panels'; describe('Settings Panels', () => { preloadFixtures('groups/edit.html'); @@ -20,11 +20,11 @@ describe('Settings Panels', () => { // Our test environment automatically expands everything so we need to clear that out first panel.classList.remove('expanded'); - expect(panel.classList.contains('expanded')).toBe(false); + expect(isExpanded(panel)).toBe(false); initSettingsPanels(); - expect(panel.classList.contains('expanded')).toBe(true); + expect(isExpanded(panel)).toBe(true); }); }); @@ -35,11 +35,11 @@ describe('Settings Panels', () => { initSettingsPanels(); - expect(panel.classList.contains('expanded')).toBe(true); + expect(isExpanded(panel)).toBe(true); $(trigger).click(); - expect(panel.classList.contains('expanded')).toBe(false); + expect(isExpanded(panel)).toBe(false); expect(trigger.textContent).toEqual(originalText); }); }); diff --git a/spec/lib/gitlab/database/migrations/instrumentation_spec.rb b/spec/lib/gitlab/database/migrations/instrumentation_spec.rb new file mode 100644 index 00000000000..d7d32123975 --- /dev/null +++ b/spec/lib/gitlab/database/migrations/instrumentation_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Gitlab::Database::Migrations::Instrumentation do + describe '#observe' do + subject { described_class.new } + + let(:migration) { 1234 } + + it 'executes the given block' do + expect { |b| subject.observe(migration, &b) }.to yield_control + end + + context 'on successful execution' do + subject { described_class.new.observe(migration) {} } + + it 'records walltime' do + expect(subject.walltime).not_to be_nil + end + + it 'records success' do + expect(subject.success).to be_truthy + end + + it 'records the migration version' do + expect(subject.migration).to eq(migration) + end + end + + context 'upon failure' do + subject { described_class.new.observe(migration) { raise 'something went wrong' } } + + it 'raises the exception' do + expect { subject }.to raise_error(/something went wrong/) + end + + context 'retrieving observations' do + subject { instance.observations.first } + + before do + instance.observe(migration) { raise 'something went wrong' } + rescue + # ignore + end + + let(:instance) { described_class.new } + + it 'records walltime' do + expect(subject.walltime).not_to be_nil + end + + it 'records failure' do + expect(subject.success).to be_falsey + end + + it 'records the migration version' do + expect(subject.migration).to eq(migration) + end + end + end + + context 'sequence of migrations with failures' do + subject { described_class.new } + + let(:migration1) { double('migration1', call: nil) } + let(:migration2) { double('migration2', call: nil) } + + it 'records observations for all migrations' do + subject.observe('migration1') {} + subject.observe('migration2') { raise 'something went wrong' } rescue nil + + expect(subject.observations.size).to eq(2) + end + end + end +end diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb index edfdb44022b..d1f4a12d8fc 100644 --- a/spec/tasks/gitlab/db_rake_spec.rb +++ b/spec/tasks/gitlab/db_rake_spec.rb @@ -297,6 +297,57 @@ RSpec.describe 'gitlab:db namespace rake task' do end end + describe '#migrate_with_instrumentation' do + subject { run_rake_task('gitlab:db:migration_testing', "[#{filename}]") } + + let(:ctx) { double('ctx', migrations: all_migrations, schema_migration: double, get_all_versions: existing_versions) } + let(:instrumentation) { instance_double(Gitlab::Database::Migrations::Instrumentation, observations: observations) } + let(:existing_versions) { [1] } + let(:all_migrations) { [double('migration1', version: 1), pending_migration] } + let(:pending_migration) { double('migration2', version: 2) } + let(:filename) { 'results-file.json'} + let(:buffer) { StringIO.new } + let(:observations) { %w[some data] } + + before do + allow(ActiveRecord::Base.connection).to receive(:migration_context).and_return(ctx) + allow(Gitlab::Database::Migrations::Instrumentation).to receive(:new).and_return(instrumentation) + allow(ActiveRecord::Migrator).to receive_message_chain('new.run').with(any_args).with(no_args) + + allow(instrumentation).to receive(:observe).and_yield + + allow(File).to receive(:open).with(filename, 'wb+').and_yield(buffer) + end + + it 'fails when given no filename argument' do + expect { run_rake_task('gitlab:db:migration_testing') }.to raise_error(/specify result_file/) + end + + it 'fails when the given file already exists' do + expect(File).to receive(:exist?).with(filename).and_return(true) + + expect { subject }.to raise_error(/File exists/) + end + + it 'instruments the pending migration' do + expect(instrumentation).to receive(:observe).with(2).and_yield + + subject + end + + it 'executes the pending migration' do + expect(ActiveRecord::Migrator).to receive_message_chain('new.run').with(:up, ctx.migrations, ctx.schema_migration, pending_migration.version).with(no_args) + + subject + end + + it 'writes observations out to JSON file' do + subject + + expect(buffer.string).to eq(observations.to_json) + end + end + def run_rake_task(task_name, arguments = '') Rake::Task[task_name].reenable Rake.application.invoke_task("#{task_name}#{arguments}") diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb index 0b06309deff..6752bdc8337 100644 --- a/spec/views/layouts/_head.html.haml_spec.rb +++ b/spec/views/layouts/_head.html.haml_spec.rb @@ -92,7 +92,8 @@ RSpec.describe 'layouts/_head' do before do stub_config(extra: { matomo_url: matomo_host, - matomo_site_id: 12345 + matomo_site_id: 12345, + matomo_disable_cookies: false }) end @@ -101,43 +102,18 @@ RSpec.describe 'layouts/_head' do expect(rendered).to match(/<script.*>.*var u="\/\/#{matomo_host}\/".*<\/script>/m) expect(rendered).to match(%r(<noscript>.*<img src="//#{matomo_host}/matomo.php.*</noscript>)) + expect(rendered).not_to include('_paq.push(["disableCookies"])') end - context 'matomo_disable_cookies' do - context 'when true' do - before do - stub_config(extra: { matomo_url: matomo_host, matomo_site_id: 12345, matomo_disable_cookies: true }) - end - - it 'disables cookies' do - render - - expect(rendered).to include('_paq.push(["disableCookies"])') - end + context 'when matomo_disable_cookies is true' do + before do + stub_config(extra: { matomo_url: matomo_host, matomo_site_id: 12345, matomo_disable_cookies: true }) end - context 'when false' do - before do - stub_config(extra: { matomo_url: matomo_host, matomo_site_id: 12345, matomo_disable_cookies: false }) - end - - it 'does not disable cookies' do - render - - expect(rendered).not_to include('_paq.push(["disableCookies"])') - end - end - - context 'when absent' do - before do - stub_config(extra: { matomo_url: matomo_host, matomo_site_id: 12345 }) - end - - it 'does not disable cookies' do - render + it 'disables cookies' do + render - expect(rendered).not_to include('_paq.push(["disableCookies"])') - end + expect(rendered).to include('_paq.push(["disableCookies"])') end end end |