summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-10 21:09:24 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-10 21:09:24 +0000
commit02c3b2af448be6a5004e8d833cbcbf8e5f185210 (patch)
tree27359dc5c21a8901c9eb95a0101cb97087b1f4ac /spec
parent577bb49691b11bc8ebae3a4966153ed39af60d87 (diff)
downloadgitlab-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.js9
-rw-r--r--spec/frontend/pipelines/test_reports/stores/utils_spec.js16
-rw-r--r--spec/frontend/pipelines/test_reports/test_suite_table_spec.js10
-rw-r--r--spec/frontend/search_settings/components/search_settings_spec.js100
-rw-r--r--spec/frontend/search_settings/mount_spec.js5
-rw-r--r--spec/frontend/settings_panels_spec.js10
-rw-r--r--spec/lib/gitlab/database/migrations/instrumentation_spec.rb76
-rw-r--r--spec/tasks/gitlab/db_rake_spec.rb51
-rw-r--r--spec/views/layouts/_head.html.haml_spec.rb42
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