summaryrefslogtreecommitdiff
path: root/spec/javascripts
diff options
context:
space:
mode:
Diffstat (limited to 'spec/javascripts')
-rw-r--r--spec/javascripts/deploy_keys/components/action_btn_spec.js64
-rw-r--r--spec/javascripts/deploy_keys/components/app_spec.js126
-rw-r--r--spec/javascripts/deploy_keys/components/key_spec.js102
-rw-r--r--spec/javascripts/deploy_keys/components/keys_panel_spec.js42
-rw-r--r--spec/javascripts/fixtures/deploy_keys.rb4
-rw-r--r--spec/javascripts/fixtures/mini_dropdown_graph.html.haml1
-rw-r--r--spec/javascripts/gpg_badges_spec.js4
-rw-r--r--spec/javascripts/ide/components/activity_bar_spec.js92
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js40
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/form_spec.js146
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_spec.js41
-rw-r--r--spec/javascripts/ide/components/ide_context_bar_spec.js37
-rw-r--r--spec/javascripts/ide/components/ide_external_links_spec.js43
-rw-r--r--spec/javascripts/ide/components/ide_project_tree_spec.js39
-rw-r--r--spec/javascripts/ide/components/ide_repo_tree_spec.js43
-rw-r--r--spec/javascripts/ide/components/ide_review_spec.js69
-rw-r--r--spec/javascripts/ide/components/ide_side_bar_spec.js33
-rw-r--r--spec/javascripts/ide/components/ide_spec.js9
-rw-r--r--spec/javascripts/ide/components/ide_status_bar_spec.js63
-rw-r--r--spec/javascripts/ide/components/ide_tree_list_spec.js54
-rw-r--r--spec/javascripts/ide/components/ide_tree_spec.js34
-rw-r--r--spec/javascripts/ide/components/repo_commit_section_spec.js162
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js27
-rw-r--r--spec/javascripts/ide/components/repo_file_spec.js37
-rw-r--r--spec/javascripts/ide/components/repo_tabs_spec.js54
-rw-r--r--spec/javascripts/ide/lib/editor_spec.js4
-rw-r--r--spec/javascripts/ide/mock_data.js15
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js24
-rw-r--r--spec/javascripts/ide/stores/actions/project_spec.js71
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js43
-rw-r--r--spec/javascripts/ide/stores/getters_spec.js26
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js30
-rw-r--r--spec/javascripts/ide/stores/mutations/branch_spec.js22
-rw-r--r--spec/javascripts/ide/stores/mutations/file_spec.js36
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js22
-rw-r--r--spec/javascripts/monitoring/graph/flag_spec.js19
-rw-r--r--spec/javascripts/monitoring/graph/track_line_spec.js10
-rw-r--r--spec/javascripts/monitoring/graph_path_spec.js2
-rw-r--r--spec/javascripts/monitoring/graph_spec.js7
-rw-r--r--spec/javascripts/pipelines/graph/action_component_spec.js2
-rw-r--r--spec/javascripts/pipelines/mock_data.js101
-rw-r--r--spec/javascripts/pipelines/stage_spec.js11
-rw-r--r--spec/javascripts/projects_dropdown/components/app_spec.js41
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js22
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js2
45 files changed, 1171 insertions, 705 deletions
diff --git a/spec/javascripts/deploy_keys/components/action_btn_spec.js b/spec/javascripts/deploy_keys/components/action_btn_spec.js
index 7025c3d836c..5bf72cc0018 100644
--- a/spec/javascripts/deploy_keys/components/action_btn_spec.js
+++ b/spec/javascripts/deploy_keys/components/action_btn_spec.js
@@ -7,62 +7,64 @@ describe('Deploy keys action btn', () => {
const deployKey = data.enabled_keys[0];
let vm;
- beforeEach((done) => {
- const ActionBtnComponent = Vue.extend(actionBtn);
-
- vm = new ActionBtnComponent({
- propsData: {
- deployKey,
- type: 'enable',
+ beforeEach(done => {
+ const ActionBtnComponent = Vue.extend({
+ components: {
+ actionBtn,
+ },
+ data() {
+ return {
+ deployKey,
+ };
},
- }).$mount();
+ template: `
+ <action-btn
+ :deploy-key="deployKey"
+ type="enable">
+ Enable
+ </action-btn>`,
+ });
+
+ vm = new ActionBtnComponent().$mount();
- setTimeout(done);
+ Vue.nextTick()
+ .then(done)
+ .catch(done.fail);
});
- it('renders the type as uppercase', () => {
- expect(
- vm.$el.textContent.trim(),
- ).toBe('Enable');
+ it('renders the default slot', () => {
+ expect(vm.$el.textContent.trim()).toBe('Enable');
});
- it('sends eventHub event with btn type', (done) => {
+ it('sends eventHub event with btn type', done => {
spyOn(eventHub, '$emit');
vm.$el.click();
- setTimeout(() => {
- expect(
- eventHub.$emit,
- ).toHaveBeenCalledWith('enable.key', deployKey, jasmine.anything());
+ Vue.nextTick(() => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('enable.key', deployKey, jasmine.anything());
done();
});
});
- it('shows loading spinner after click', (done) => {
+ it('shows loading spinner after click', done => {
vm.$el.click();
- setTimeout(() => {
- expect(
- vm.$el.querySelector('.fa'),
- ).toBeDefined();
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.fa')).toBeDefined();
done();
});
});
- it('disables button after click', (done) => {
+ it('disables button after click', done => {
vm.$el.click();
- setTimeout(() => {
- expect(
- vm.$el.classList.contains('disabled'),
- ).toBeTruthy();
+ Vue.nextTick(() => {
+ expect(vm.$el.classList.contains('disabled')).toBeTruthy();
- expect(
- vm.$el.getAttribute('disabled'),
- ).toBe('disabled');
+ expect(vm.$el.getAttribute('disabled')).toBe('disabled');
done();
});
diff --git a/spec/javascripts/deploy_keys/components/app_spec.js b/spec/javascripts/deploy_keys/components/app_spec.js
index b870f87eab9..3f9e25a8862 100644
--- a/spec/javascripts/deploy_keys/components/app_spec.js
+++ b/spec/javascripts/deploy_keys/components/app_spec.js
@@ -8,12 +8,14 @@ describe('Deploy keys app component', () => {
let vm;
const deployKeysResponse = (request, next) => {
- next(request.respondWith(JSON.stringify(data), {
- status: 200,
- }));
+ next(
+ request.respondWith(JSON.stringify(data), {
+ status: 200,
+ }),
+ );
};
- beforeEach((done) => {
+ beforeEach(done => {
const Component = Vue.extend(deployKeysApp);
Vue.http.interceptors.push(deployKeysResponse);
@@ -21,6 +23,7 @@ describe('Deploy keys app component', () => {
vm = new Component({
propsData: {
endpoint: '/test',
+ projectId: '8',
},
}).$mount();
@@ -31,117 +34,112 @@ describe('Deploy keys app component', () => {
Vue.http.interceptors = _.without(Vue.http.interceptors, deployKeysResponse);
});
- it('renders loading icon', (done) => {
+ it('renders loading icon', done => {
vm.store.keys = {};
vm.isLoading = false;
Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.deploy-keys-panel').length,
- ).toBe(0);
+ expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(0);
- expect(
- vm.$el.querySelector('.fa-spinner'),
- ).toBeDefined();
+ expect(vm.$el.querySelector('.fa-spinner')).toBeDefined();
done();
});
});
it('renders keys panels', () => {
- expect(
- vm.$el.querySelectorAll('.deploy-keys-panel').length,
- ).toBe(3);
+ expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(3);
});
- it('does not render key panels when keys object is empty', (done) => {
- vm.store.keys = {};
-
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.deploy-keys-panel').length,
- ).toBe(0);
-
- done();
- });
+ it('renders the titles with keys count', () => {
+ const textContent = selector => {
+ const element = vm.$el.querySelector(`${selector}`);
+
+ expect(element).not.toBeNull();
+ return element.textContent.trim();
+ };
+
+ expect(textContent('.js-deployKeys-tab-enabled_keys')).toContain('Enabled deploy keys');
+ expect(textContent('.js-deployKeys-tab-available_project_keys')).toContain(
+ 'Privately accessible deploy keys',
+ );
+ expect(textContent('.js-deployKeys-tab-public_keys')).toContain(
+ 'Publicly accessible deploy keys',
+ );
+
+ expect(textContent('.js-deployKeys-tab-enabled_keys .badge')).toBe(
+ `${vm.store.keys.enabled_keys.length}`,
+ );
+ expect(textContent('.js-deployKeys-tab-available_project_keys .badge')).toBe(
+ `${vm.store.keys.available_project_keys.length}`,
+ );
+ expect(textContent('.js-deployKeys-tab-public_keys .badge')).toBe(
+ `${vm.store.keys.public_keys.length}`,
+ );
});
- it('does not render public panel when empty', (done) => {
- vm.store.keys.public_keys = [];
+ it('does not render key panels when keys object is empty', done => {
+ vm.store.keys = {};
Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.deploy-keys-panel').length,
- ).toBe(2);
+ expect(vm.$el.querySelectorAll('.deploy-keys .nav-links li').length).toBe(0);
done();
});
});
- it('re-fetches deploy keys when enabling a key', (done) => {
+ it('re-fetches deploy keys when enabling a key', done => {
const key = data.public_keys[0];
spyOn(vm.service, 'getKeys');
- spyOn(vm.service, 'enableKey').and.callFake(() => new Promise((resolve) => {
- resolve();
-
- setTimeout(() => {
- expect(vm.service.getKeys).toHaveBeenCalled();
-
- done();
- });
- }));
+ spyOn(vm.service, 'enableKey').and.callFake(() => Promise.resolve());
eventHub.$emit('enable.key', key);
- expect(vm.service.enableKey).toHaveBeenCalledWith(key.id);
+ Vue.nextTick(() => {
+ expect(vm.service.enableKey).toHaveBeenCalledWith(key.id);
+ expect(vm.service.getKeys).toHaveBeenCalled();
+ done();
+ });
});
- it('re-fetches deploy keys when disabling a key', (done) => {
+ it('re-fetches deploy keys when disabling a key', done => {
const key = data.public_keys[0];
spyOn(window, 'confirm').and.returnValue(true);
spyOn(vm.service, 'getKeys');
- spyOn(vm.service, 'disableKey').and.callFake(() => new Promise((resolve) => {
- resolve();
-
- setTimeout(() => {
- expect(vm.service.getKeys).toHaveBeenCalled();
-
- done();
- });
- }));
+ spyOn(vm.service, 'disableKey').and.callFake(() => Promise.resolve());
eventHub.$emit('disable.key', key);
- expect(vm.service.disableKey).toHaveBeenCalledWith(key.id);
+ Vue.nextTick(() => {
+ expect(vm.service.disableKey).toHaveBeenCalledWith(key.id);
+ expect(vm.service.getKeys).toHaveBeenCalled();
+ done();
+ });
});
- it('calls disableKey when removing a key', (done) => {
+ it('calls disableKey when removing a key', done => {
const key = data.public_keys[0];
spyOn(window, 'confirm').and.returnValue(true);
spyOn(vm.service, 'getKeys');
- spyOn(vm.service, 'disableKey').and.callFake(() => new Promise((resolve) => {
- resolve();
-
- setTimeout(() => {
- expect(vm.service.getKeys).toHaveBeenCalled();
-
- done();
- });
- }));
+ spyOn(vm.service, 'disableKey').and.callFake(() => Promise.resolve());
eventHub.$emit('remove.key', key);
- expect(vm.service.disableKey).toHaveBeenCalledWith(key.id);
+ Vue.nextTick(() => {
+ expect(vm.service.disableKey).toHaveBeenCalledWith(key.id);
+ expect(vm.service.getKeys).toHaveBeenCalled();
+ done();
+ });
});
it('hasKeys returns true when there are keys', () => {
expect(vm.hasKeys).toEqual(3);
});
- it('resets remove button loading state', (done) => {
+ it('resets disable button loading state', done => {
spyOn(window, 'confirm').and.returnValue(false);
const btn = vm.$el.querySelector('.btn-warning');
@@ -149,7 +147,7 @@ describe('Deploy keys app component', () => {
btn.click();
Vue.nextTick(() => {
- expect(btn.querySelector('.fa')).toBeNull();
+ expect(btn.querySelector('.btn-warning')).not.toExist();
done();
});
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index b7aadf604a4..4279add21d1 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -7,7 +7,7 @@ describe('Deploy keys key', () => {
let vm;
const KeyComponent = Vue.extend(key);
const data = getJSONFixture('deploy_keys/keys.json');
- const createComponent = (deployKey) => {
+ const createComponent = deployKey => {
const store = new DeployKeysStore();
store.keys = data;
@@ -23,37 +23,42 @@ describe('Deploy keys key', () => {
describe('enabled key', () => {
const deployKey = data.enabled_keys[0];
- beforeEach((done) => {
+ beforeEach(done => {
createComponent(deployKey);
setTimeout(done);
});
it('renders the keys title', () => {
- expect(
- vm.$el.querySelector('.title').textContent.trim(),
- ).toContain('My title');
+ expect(vm.$el.querySelector('.title').textContent.trim()).toContain('My title');
});
it('renders human friendly formatted created date', () => {
- expect(
- vm.$el.querySelector('.key-created-at').textContent.trim(),
- ).toBe(`created ${getTimeago().format(deployKey.created_at)}`);
+ expect(vm.$el.querySelector('.key-created-at').textContent.trim()).toBe(
+ `${getTimeago().format(deployKey.created_at)}`,
+ );
});
- it('shows edit button', () => {
- expect(
- vm.$el.querySelectorAll('.btn')[0].textContent.trim(),
- ).toBe('Edit');
+ it('shows pencil button for editing', () => {
+ expect(vm.$el.querySelector('.btn .ic-pencil')).toExist();
});
- it('shows remove button', () => {
- expect(
- vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
- ).toBe('Remove');
+ it('shows disable button when the project is not deletable', () => {
+ expect(vm.$el.querySelector('.btn .ic-cancel')).toExist();
});
- it('shows write access title when key has write access', (done) => {
+ it('shows remove button when the project is deletable', done => {
+ vm.deployKey.destroyed_when_orphaned = true;
+ vm.deployKey.almost_orphaned = true;
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.btn .ic-remove')).toExist();
+ done();
+ });
+ });
+ });
+
+ describe('deploy key labels', () => {
+ it('shows write access title when key has write access', done => {
vm.deployKey.deploy_keys_projects[0].can_push = true;
Vue.nextTick(() => {
@@ -64,7 +69,7 @@ describe('Deploy keys key', () => {
});
});
- it('does not show write access title when key has write access', (done) => {
+ it('does not show write access title when key has write access', done => {
vm.deployKey.deploy_keys_projects[0].can_push = false;
Vue.nextTick(() => {
@@ -74,36 +79,73 @@ describe('Deploy keys key', () => {
done();
});
});
+
+ it('shows expandable button if more than two projects', () => {
+ const labels = vm.$el.querySelectorAll('.deploy-project-label');
+ expect(labels.length).toBe(2);
+ expect(labels[1].textContent).toContain('others');
+ expect(labels[1].getAttribute('data-original-title')).toContain('Expand');
+ });
+
+ it('expands all project labels after click', done => {
+ const length = vm.deployKey.deploy_keys_projects.length;
+ vm.$el.querySelectorAll('.deploy-project-label')[1].click();
+
+ Vue.nextTick(() => {
+ const labels = vm.$el.querySelectorAll('.deploy-project-label');
+ expect(labels.length).toBe(length);
+ expect(labels[1].textContent).not.toContain(`+${length} others`);
+ expect(labels[1].getAttribute('data-original-title')).not.toContain('Expand');
+ done();
+ });
+ });
+
+ it('shows two projects', done => {
+ vm.deployKey.deploy_keys_projects = [...vm.deployKey.deploy_keys_projects].slice(0, 2);
+
+ Vue.nextTick(() => {
+ const labels = vm.$el.querySelectorAll('.deploy-project-label');
+ expect(labels.length).toBe(2);
+ expect(labels[1].textContent).toContain(
+ vm.deployKey.deploy_keys_projects[1].project.full_name,
+ );
+ done();
+ });
+ });
});
describe('public keys', () => {
const deployKey = data.public_keys[0];
- beforeEach((done) => {
+ beforeEach(done => {
createComponent(deployKey);
setTimeout(done);
});
- it('shows edit button', () => {
- expect(
- vm.$el.querySelectorAll('.btn')[0].textContent.trim(),
- ).toBe('Edit');
+ it('renders deploy keys without any enabled projects', done => {
+ vm.deployKey.deploy_keys_projects = [];
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.deploy-project-list').textContent.trim()).toBe('None');
+
+ done();
+ });
});
it('shows enable button', () => {
- expect(
- vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
- ).toBe('Enable');
+ expect(vm.$el.querySelectorAll('.btn')[0].textContent.trim()).toBe('Enable');
});
- it('shows disable button when key is enabled', (done) => {
+ it('shows pencil button for editing', () => {
+ expect(vm.$el.querySelector('.btn .ic-pencil')).toExist();
+ });
+
+ it('shows disable button when key is enabled', done => {
vm.store.keys.enabled_keys.push(deployKey);
Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.btn')[1].textContent.trim(),
- ).toBe('Disable');
+ expect(vm.$el.querySelector('.btn .ic-cancel')).toExist();
done();
});
diff --git a/spec/javascripts/deploy_keys/components/keys_panel_spec.js b/spec/javascripts/deploy_keys/components/keys_panel_spec.js
index 08357d2b547..f71f5ccf082 100644
--- a/spec/javascripts/deploy_keys/components/keys_panel_spec.js
+++ b/spec/javascripts/deploy_keys/components/keys_panel_spec.js
@@ -6,7 +6,7 @@ describe('Deploy keys panel', () => {
const data = getJSONFixture('deploy_keys/keys.json');
let vm;
- beforeEach((done) => {
+ beforeEach(done => {
const DeployKeysPanelComponent = Vue.extend(deployKeysPanel);
const store = new DeployKeysStore();
store.keys = data;
@@ -24,46 +24,38 @@ describe('Deploy keys panel', () => {
setTimeout(done);
});
- it('renders the title with keys count', () => {
- expect(
- vm.$el.querySelector('h5').textContent.trim(),
- ).toContain('test');
-
- expect(
- vm.$el.querySelector('h5').textContent.trim(),
- ).toContain(`(${vm.keys.length})`);
+ it('renders list of keys', () => {
+ expect(vm.$el.querySelectorAll('.deploy-key').length).toBe(vm.keys.length);
});
- it('renders list of keys', () => {
- expect(
- vm.$el.querySelectorAll('li').length,
- ).toBe(vm.keys.length);
+ it('renders table header', () => {
+ const tableHeader = vm.$el.querySelector('.table-row-header');
+
+ expect(tableHeader).toExist();
+ expect(tableHeader.textContent).toContain('Deploy key');
+ expect(tableHeader.textContent).toContain('Project usage');
+ expect(tableHeader.textContent).toContain('Created');
});
- it('renders help box if keys are empty', (done) => {
+ it('renders help box if keys are empty', done => {
vm.keys = [];
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.settings-message'),
- ).toBeDefined();
+ expect(vm.$el.querySelector('.settings-message')).toBeDefined();
- expect(
- vm.$el.querySelector('.settings-message').textContent.trim(),
- ).toBe('No deploy keys found. Create one with the form above.');
+ expect(vm.$el.querySelector('.settings-message').textContent.trim()).toBe(
+ 'No deploy keys found. Create one with the form above.',
+ );
done();
});
});
- it('does not render help box if keys are empty & showHelpBox is false', (done) => {
+ it('renders no table header if keys are empty', done => {
vm.keys = [];
- vm.showHelpBox = false;
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.settings-message'),
- ).toBeNull();
+ expect(vm.$el.querySelector('.table-row-header')).not.toExist();
done();
});
diff --git a/spec/javascripts/fixtures/deploy_keys.rb b/spec/javascripts/fixtures/deploy_keys.rb
index 580894ceaf9..24699c3043a 100644
--- a/spec/javascripts/fixtures/deploy_keys.rb
+++ b/spec/javascripts/fixtures/deploy_keys.rb
@@ -7,6 +7,8 @@ describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :control
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'todos-project') }
let(:project2) { create(:project, :internal)}
+ let(:project3) { create(:project, :internal)}
+ let(:project4) { create(:project, :internal)}
before(:all) do
clean_frontend_fixtures('deploy_keys/')
@@ -28,6 +30,8 @@ describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :control
internal_key = create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com')
create(:deploy_keys_project, project: project, deploy_key: project_key)
create(:deploy_keys_project, project: project2, deploy_key: internal_key)
+ create(:deploy_keys_project, project: project3, deploy_key: project_key)
+ create(:deploy_keys_project, project: project4, deploy_key: project_key)
get :index,
namespace_id: project.namespace.to_param,
diff --git a/spec/javascripts/fixtures/mini_dropdown_graph.html.haml b/spec/javascripts/fixtures/mini_dropdown_graph.html.haml
index b532b48a95b..74584993739 100644
--- a/spec/javascripts/fixtures/mini_dropdown_graph.html.haml
+++ b/spec/javascripts/fixtures/mini_dropdown_graph.html.haml
@@ -4,6 +4,7 @@
%ul.dropdown-menu.mini-pipeline-graph-dropdown-menu.js-builds-dropdown-container
%li.js-builds-dropdown-list.scrollable-menu
+ %ul
%li.js-builds-dropdown-loading.hidden
%span.fa.fa-spinner
diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js
index 5decb5e6bbd..97c771dcfd3 100644
--- a/spec/javascripts/gpg_badges_spec.js
+++ b/spec/javascripts/gpg_badges_spec.js
@@ -16,8 +16,8 @@ describe('GpgBadges', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
setFixtures(`
- <form
- class="commits-search-form" data-signatures-path="/hello" action="/hello"
+ <form
+ class="commits-search-form js-signature-container" data-signatures-path="/hello" action="/hello"
method="get">
<input name="utf8" type="hidden" value="✓">
<input type="search" name="search" id="commits-search"class="form-control search-text-input input-short">
diff --git a/spec/javascripts/ide/components/activity_bar_spec.js b/spec/javascripts/ide/components/activity_bar_spec.js
new file mode 100644
index 00000000000..946c7e8e9c8
--- /dev/null
+++ b/spec/javascripts/ide/components/activity_bar_spec.js
@@ -0,0 +1,92 @@
+import Vue from 'vue';
+import store from '~/ide/stores';
+import { activityBarViews } from '~/ide/constants';
+import ActivityBar from '~/ide/components/activity_bar.vue';
+import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { resetStore } from '../helpers';
+
+describe('IDE activity bar', () => {
+ const Component = Vue.extend(ActivityBar);
+ let vm;
+
+ beforeEach(() => {
+ Vue.set(store.state.projects, 'abcproject', {
+ web_url: 'testing',
+ });
+ Vue.set(store.state, 'currentProjectId', 'abcproject');
+
+ vm = createComponentWithStore(Component, store);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ describe('goBackUrl', () => {
+ it('renders the Go Back link with the referrer when present', () => {
+ const fakeReferrer = '/example/README.md';
+ spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
+
+ vm.$mount();
+
+ expect(vm.goBackUrl).toEqual(fakeReferrer);
+ });
+
+ it('renders the Go Back link with the project url when referrer is not present', () => {
+ const fakeReferrer = '';
+ spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
+
+ vm.$mount();
+
+ expect(vm.goBackUrl).toEqual('testing');
+ });
+ });
+
+ describe('updateActivityBarView', () => {
+ beforeEach(() => {
+ spyOn(vm, 'updateActivityBarView');
+
+ vm.$mount();
+ });
+
+ it('calls updateActivityBarView with edit value on click', () => {
+ vm.$el.querySelector('.js-ide-edit-mode').click();
+
+ expect(vm.updateActivityBarView).toHaveBeenCalledWith(activityBarViews.edit);
+ });
+
+ it('calls updateActivityBarView with commit value on click', () => {
+ vm.$el.querySelector('.js-ide-commit-mode').click();
+
+ expect(vm.updateActivityBarView).toHaveBeenCalledWith(activityBarViews.commit);
+ });
+
+ it('calls updateActivityBarView with review value on click', () => {
+ vm.$el.querySelector('.js-ide-review-mode').click();
+
+ expect(vm.updateActivityBarView).toHaveBeenCalledWith(activityBarViews.review);
+ });
+ });
+
+ describe('active item', () => {
+ beforeEach(() => {
+ vm.$mount();
+ });
+
+ it('sets edit item active', () => {
+ expect(vm.$el.querySelector('.js-ide-edit-mode').classList).toContain('active');
+ });
+
+ it('sets commit item active', done => {
+ vm.$store.state.currentActivityView = activityBarViews.commit;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.js-ide-commit-mode').classList).toContain('active');
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js b/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js
index 53275b78da5..16d0b354a30 100644
--- a/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js
@@ -10,10 +10,9 @@ describe('IDE commit panel empty state', () => {
beforeEach(() => {
const Component = Vue.extend(emptyState);
- vm = createComponentWithStore(Component, store, {
- noChangesStateSvgPath: 'no-changes',
- committedStateSvgPath: 'committed-state',
- });
+ Vue.set(store.state, 'noChangesStateSvgPath', 'no-changes');
+
+ vm = createComponentWithStore(Component, store);
vm.$mount();
});
@@ -27,37 +26,4 @@ describe('IDE commit panel empty state', () => {
it('renders no changes text when last commit message is empty', () => {
expect(vm.$el.textContent).toContain('No changes');
});
-
- describe('toggle button', () => {
- it('calls store action', () => {
- spyOn(vm, 'toggleRightPanelCollapsed');
-
- vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
-
- expect(vm.toggleRightPanelCollapsed).toHaveBeenCalled();
- });
-
- it('renders collapsed class', done => {
- vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
-
- Vue.nextTick(() => {
- expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
-
- done();
- });
- });
- });
-
- describe('collapsed state', () => {
- beforeEach(done => {
- vm.$store.state.rightPanelCollapsed = true;
-
- Vue.nextTick(done);
- });
-
- it('does not render text & svg', () => {
- expect(vm.$el.querySelector('img')).toBeNull();
- expect(vm.$el.textContent).not.toContain('No changes');
- });
- });
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/form_spec.js b/spec/javascripts/ide/components/commit_sidebar/form_spec.js
new file mode 100644
index 00000000000..ce7c134bc97
--- /dev/null
+++ b/spec/javascripts/ide/components/commit_sidebar/form_spec.js
@@ -0,0 +1,146 @@
+import Vue from 'vue';
+import store from '~/ide/stores';
+import CommitForm from '~/ide/components/commit_sidebar/form.vue';
+import { activityBarViews } from '~/ide/constants';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
+import { resetStore } from '../../helpers';
+
+describe('IDE commit form', () => {
+ const Component = Vue.extend(CommitForm);
+ let vm;
+
+ beforeEach(() => {
+ spyOnProperty(window, 'innerHeight').and.returnValue(800);
+
+ store.state.changedFiles.push('test');
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ it('enables button when has changes', () => {
+ expect(vm.$el.querySelector('[disabled]')).toBe(null);
+ });
+
+ describe('compact', () => {
+ it('renders commit button in compact mode', () => {
+ expect(vm.$el.querySelector('.btn-primary')).not.toBeNull();
+ expect(vm.$el.querySelector('.btn-primary').textContent).toContain('Commit');
+ });
+
+ it('does not render form', () => {
+ expect(vm.$el.querySelector('form')).toBeNull();
+ });
+
+ it('renders overview text', done => {
+ vm.$store.state.stagedFiles.push('test');
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('p').textContent).toContain('1 unstaged and 1 staged changes');
+ done();
+ });
+ });
+
+ it('shows form when clicking commit button', done => {
+ vm.$el.querySelector('.btn-primary').click();
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('form')).not.toBeNull();
+
+ done();
+ });
+ });
+
+ it('toggles activity bar vie when clicking commit button', done => {
+ vm.$el.querySelector('.btn-primary').click();
+
+ vm.$nextTick(() => {
+ expect(store.state.currentActivityView).toBe(activityBarViews.commit);
+
+ done();
+ });
+ });
+ });
+
+ describe('full', () => {
+ beforeEach(done => {
+ vm.isCompact = false;
+
+ vm.$nextTick(done);
+ });
+
+ it('updates commitMessage in store on input', done => {
+ const textarea = vm.$el.querySelector('textarea');
+
+ textarea.value = 'testing commit message';
+
+ textarea.dispatchEvent(new Event('input'));
+
+ getSetTimeoutPromise()
+ .then(() => {
+ expect(vm.$store.state.commit.commitMessage).toBe('testing commit message');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('updating currentActivityView not to commit view sets compact mode', done => {
+ store.state.currentActivityView = 'a';
+
+ vm.$nextTick(() => {
+ expect(vm.isCompact).toBe(true);
+
+ done();
+ });
+ });
+
+ describe('discard draft button', () => {
+ it('hidden when commitMessage is empty', () => {
+ expect(vm.$el.querySelector('.btn-default').textContent).toContain('Collapse');
+ });
+
+ it('resets commitMessage when clicking discard button', done => {
+ vm.$store.state.commit.commitMessage = 'testing commit message';
+
+ getSetTimeoutPromise()
+ .then(() => {
+ vm.$el.querySelector('.btn-default').click();
+ })
+ .then(Vue.nextTick)
+ .then(() => {
+ expect(vm.$store.state.commit.commitMessage).not.toBe('testing commit message');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('when submitting', () => {
+ beforeEach(() => {
+ spyOn(vm, 'commitChanges');
+ vm.$store.state.stagedFiles.push('test');
+ });
+
+ it('calls commitChanges', done => {
+ vm.$store.state.commit.commitMessage = 'testing commit message';
+
+ getSetTimeoutPromise()
+ .then(() => {
+ vm.$el.querySelector('.btn-success').click();
+ })
+ .then(Vue.nextTick)
+ .then(() => {
+ expect(vm.commitChanges).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
index 62fc3f90ad1..54625ef90f8 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
@@ -49,45 +49,4 @@ describe('Multi-file editor commit sidebar list', () => {
expect(vm.$el.textContent).toContain('No changes');
});
});
-
- describe('collapsed', () => {
- beforeEach(done => {
- vm.$store.state.rightPanelCollapsed = true;
-
- Vue.nextTick(done);
- });
-
- it('hides list', () => {
- expect(vm.$el.querySelector('.list-unstyled')).toBeNull();
- expect(vm.$el.querySelector('.help-block')).toBeNull();
- });
- });
-
- describe('with toggle', () => {
- beforeEach(done => {
- spyOn(vm, 'toggleRightPanelCollapsed');
-
- vm.showToggle = true;
-
- Vue.nextTick(done);
- });
-
- it('calls setPanelCollapsedStatus when clickin toggle', () => {
- vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
-
- expect(vm.toggleRightPanelCollapsed).toHaveBeenCalled();
- });
- });
-
- describe('action button', () => {
- beforeEach(() => {
- spyOn(vm, 'stageAllChanges');
- });
-
- it('calls store action when clicked', () => {
- vm.$el.querySelector('.ide-staged-action-btn').click();
-
- expect(vm.stageAllChanges).toHaveBeenCalled();
- });
- });
});
diff --git a/spec/javascripts/ide/components/ide_context_bar_spec.js b/spec/javascripts/ide/components/ide_context_bar_spec.js
deleted file mode 100644
index e17b051f137..00000000000
--- a/spec/javascripts/ide/components/ide_context_bar_spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import Vue from 'vue';
-import store from '~/ide/stores';
-import ideContextBar from '~/ide/components/ide_context_bar.vue';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-
-describe('Multi-file editor right context bar', () => {
- let vm;
-
- beforeEach(() => {
- const Component = Vue.extend(ideContextBar);
-
- vm = createComponentWithStore(Component, store, {
- noChangesStateSvgPath: 'svg',
- committedStateSvgPath: 'svg',
- });
-
- vm.$store.state.rightPanelCollapsed = false;
-
- vm.$mount();
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('collapsed', () => {
- beforeEach(done => {
- vm.$store.state.rightPanelCollapsed = true;
-
- Vue.nextTick(done);
- });
-
- it('adds collapsed class', () => {
- expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
- });
- });
-});
diff --git a/spec/javascripts/ide/components/ide_external_links_spec.js b/spec/javascripts/ide/components/ide_external_links_spec.js
deleted file mode 100644
index 9f6cb459f3b..00000000000
--- a/spec/javascripts/ide/components/ide_external_links_spec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import Vue from 'vue';
-import ideExternalLinks from '~/ide/components/ide_external_links.vue';
-import createComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('ide external links component', () => {
- let vm;
- let fakeReferrer;
- let Component;
-
- const fakeProjectUrl = '/project/';
-
- beforeEach(() => {
- Component = Vue.extend(ideExternalLinks);
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('goBackUrl', () => {
- it('renders the Go Back link with the referrer when present', () => {
- fakeReferrer = '/example/README.md';
- spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
-
- vm = createComponent(Component, {
- projectUrl: fakeProjectUrl,
- }).$mount();
-
- expect(vm.goBackUrl).toEqual(fakeReferrer);
- });
-
- it('renders the Go Back link with the project url when referrer is not present', () => {
- fakeReferrer = '';
- spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
-
- vm = createComponent(Component, {
- projectUrl: fakeProjectUrl,
- }).$mount();
-
- expect(vm.goBackUrl).toEqual(fakeProjectUrl);
- });
- });
-});
diff --git a/spec/javascripts/ide/components/ide_project_tree_spec.js b/spec/javascripts/ide/components/ide_project_tree_spec.js
deleted file mode 100644
index 657682cb39c..00000000000
--- a/spec/javascripts/ide/components/ide_project_tree_spec.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import Vue from 'vue';
-import ProjectTree from '~/ide/components/ide_project_tree.vue';
-import createComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('IDE project tree', () => {
- const Component = Vue.extend(ProjectTree);
- let vm;
-
- beforeEach(() => {
- vm = createComponent(Component, {
- project: {
- id: 1,
- name: 'test',
- web_url: gl.TEST_HOST,
- avatar_url: '',
- branches: [],
- },
- });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders identicon when projct has no avatar', () => {
- expect(vm.$el.querySelector('.identicon')).not.toBeNull();
- });
-
- it('renders avatar image if project has avatar', done => {
- vm.project.avatar_url = gl.TEST_HOST;
-
- vm.$nextTick(() => {
- expect(vm.$el.querySelector('.identicon')).toBeNull();
- expect(vm.$el.querySelector('img.avatar')).not.toBeNull();
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/ide/components/ide_repo_tree_spec.js b/spec/javascripts/ide/components/ide_repo_tree_spec.js
deleted file mode 100644
index e0fbc90ca61..00000000000
--- a/spec/javascripts/ide/components/ide_repo_tree_spec.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import Vue from 'vue';
-import ideRepoTree from '~/ide/components/ide_repo_tree.vue';
-import createComponent from '../../helpers/vue_mount_component_helper';
-import { file } from '../helpers';
-
-describe('IdeRepoTree', () => {
- let vm;
- let tree;
-
- beforeEach(() => {
- const IdeRepoTree = Vue.extend(ideRepoTree);
-
- tree = {
- tree: [file()],
- loading: false,
- };
-
- vm = createComponent(IdeRepoTree, {
- tree,
- });
- });
-
- afterEach(() => {
- vm.$destroy();
- });
-
- it('renders a sidebar', () => {
- expect(vm.$el.querySelector('.loading-file')).toBeNull();
- expect(vm.$el.querySelector('.file')).not.toBeNull();
- });
-
- it('renders 3 loading files if tree is loading', done => {
- tree.loading = true;
-
- vm.$nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.multi-file-loading-container').length,
- ).toEqual(3);
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/ide/components/ide_review_spec.js b/spec/javascripts/ide/components/ide_review_spec.js
new file mode 100644
index 00000000000..b9ee22b7c1a
--- /dev/null
+++ b/spec/javascripts/ide/components/ide_review_spec.js
@@ -0,0 +1,69 @@
+import Vue from 'vue';
+import IdeReview from '~/ide/components/ide_review.vue';
+import store from '~/ide/stores';
+import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { trimText } from '../../helpers/vue_component_helper';
+import { resetStore, file } from '../helpers';
+import { projectData } from '../mock_data';
+
+describe('IDE review mode', () => {
+ const Component = Vue.extend(IdeReview);
+ let vm;
+
+ beforeEach(() => {
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = Object.assign({}, projectData);
+ Vue.set(store.state.trees, 'abcproject/master', {
+ tree: [file('fileName')],
+ loading: false,
+ });
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ it('renders list of files', () => {
+ expect(vm.$el.textContent).toContain('fileName');
+ });
+
+ describe('merge request', () => {
+ beforeEach(done => {
+ store.state.currentMergeRequestId = '1';
+ store.state.projects.abcproject.mergeRequests['1'] = {
+ iid: 123,
+ web_url: 'testing123',
+ };
+
+ vm.$nextTick(done);
+ });
+
+ it('renders edit dropdown', () => {
+ expect(vm.$el.querySelector('.btn')).not.toBe(null);
+ });
+
+ it('renders merge request link & IID', () => {
+ const link = vm.$el.querySelector('.ide-review-sub-header');
+
+ expect(link.querySelector('a').getAttribute('href')).toBe('testing123');
+ expect(trimText(link.textContent)).toBe('Merge request (!123)');
+ });
+
+ it('changes text to latest changes when viewer is not mrdiff', done => {
+ store.state.viewer = 'diff';
+
+ vm.$nextTick(() => {
+ expect(trimText(vm.$el.querySelector('.ide-review-sub-header').textContent)).toBe(
+ 'Latest changes',
+ );
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/ide_side_bar_spec.js b/spec/javascripts/ide/components/ide_side_bar_spec.js
index 699dae1ce2f..20ee20bc1d7 100644
--- a/spec/javascripts/ide/components/ide_side_bar_spec.js
+++ b/spec/javascripts/ide/components/ide_side_bar_spec.js
@@ -1,8 +1,10 @@
import Vue from 'vue';
import store from '~/ide/stores';
import ideSidebar from '~/ide/components/ide_side_bar.vue';
+import { activityBarViews } from '~/ide/constants';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../helpers';
+import { projectData } from '../mock_data';
describe('IdeSidebar', () => {
let vm;
@@ -10,6 +12,9 @@ describe('IdeSidebar', () => {
beforeEach(() => {
const Component = Vue.extend(ideSidebar);
+ store.state.currentProjectId = 'abcproject';
+ store.state.projects.abcproject = projectData;
+
vm = createComponentWithStore(Component, store).$mount();
});
@@ -20,23 +25,33 @@ describe('IdeSidebar', () => {
});
it('renders a sidebar', () => {
- expect(
- vm.$el.querySelector('.multi-file-commit-panel-inner'),
- ).not.toBeNull();
+ expect(vm.$el.querySelector('.multi-file-commit-panel-inner')).not.toBeNull();
});
it('renders loading icon component', done => {
vm.$store.state.loading = true;
vm.$nextTick(() => {
- expect(
- vm.$el.querySelector('.multi-file-loading-container'),
- ).not.toBeNull();
- expect(
- vm.$el.querySelectorAll('.multi-file-loading-container').length,
- ).toBe(3);
+ expect(vm.$el.querySelector('.multi-file-loading-container')).not.toBeNull();
+ expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3);
done();
});
});
+
+ describe('activityBarComponent', () => {
+ it('renders tree component', () => {
+ expect(vm.$el.querySelector('.ide-file-list')).not.toBeNull();
+ });
+
+ it('renders commit component', done => {
+ vm.$store.state.currentActivityView = activityBarViews.commit;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.multi-file-commit-panel-section')).not.toBeNull();
+
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/ide_spec.js b/spec/javascripts/ide/components/ide_spec.js
index 7bfcfc90572..6f580e1f7af 100644
--- a/spec/javascripts/ide/components/ide_spec.js
+++ b/spec/javascripts/ide/components/ide_spec.js
@@ -4,6 +4,7 @@ import store from '~/ide/stores';
import ide from '~/ide/components/ide.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file, resetStore } from '../helpers';
+import { projectData } from '../mock_data';
describe('ide component', () => {
let vm;
@@ -11,6 +12,10 @@ describe('ide component', () => {
beforeEach(() => {
const Component = Vue.extend(ide);
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = Object.assign({}, projectData);
+
vm = createComponentWithStore(Component, store, {
emptyStateSvgPath: 'svg',
noChangesStateSvgPath: 'svg',
@@ -24,11 +29,11 @@ describe('ide component', () => {
resetStore(vm.$store);
});
- it('does not render panel right when no files open', () => {
+ it('does not render right right when no files open', () => {
expect(vm.$el.querySelector('.panel-right')).toBeNull();
});
- it('renders panel right when files are open', done => {
+ it('renders right panel when files are open', done => {
vm.$store.state.trees['abcproject/mybranch'] = {
tree: [file()],
};
diff --git a/spec/javascripts/ide/components/ide_status_bar_spec.js b/spec/javascripts/ide/components/ide_status_bar_spec.js
new file mode 100644
index 00000000000..770dca9cb0f
--- /dev/null
+++ b/spec/javascripts/ide/components/ide_status_bar_spec.js
@@ -0,0 +1,63 @@
+import Vue from 'vue';
+import store from '~/ide/stores';
+import ideStatusBar from '~/ide/components/ide_status_bar.vue';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { resetStore } from '../helpers';
+import { projectData } from '../mock_data';
+
+describe('ideStatusBar', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(ideStatusBar);
+
+ store.state.currentProjectId = 'abcproject';
+ store.state.projects.abcproject = projectData;
+
+ vm = createComponentWithStore(Component, store).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ it('renders the statusbar', () => {
+ expect(vm.$el.className).toBe('ide-status-bar');
+ });
+
+ describe('mounted', () => {
+ it('triggers a setInterval', () => {
+ expect(vm.intervalId).not.toBe(null);
+ });
+ });
+
+ describe('commitAgeUpdate', () => {
+ beforeEach(function() {
+ jasmine.clock().install();
+ spyOn(vm, 'commitAgeUpdate').and.callFake(() => {});
+ vm.startTimer();
+ });
+
+ afterEach(function() {
+ jasmine.clock().uninstall();
+ });
+
+ it('gets called every second', () => {
+ expect(vm.commitAgeUpdate).not.toHaveBeenCalled();
+
+ jasmine.clock().tick(1100);
+ expect(vm.commitAgeUpdate.calls.count()).toEqual(1);
+
+ jasmine.clock().tick(1000);
+ expect(vm.commitAgeUpdate.calls.count()).toEqual(2);
+ });
+ });
+
+ describe('getCommitPath', () => {
+ it('returns the path to the commit details', () => {
+ expect(vm.getCommitPath('abc123de')).toBe('/commit/abc123de');
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/ide_tree_list_spec.js b/spec/javascripts/ide/components/ide_tree_list_spec.js
new file mode 100644
index 00000000000..4ecbdb8a55e
--- /dev/null
+++ b/spec/javascripts/ide/components/ide_tree_list_spec.js
@@ -0,0 +1,54 @@
+import Vue from 'vue';
+import IdeTreeList from '~/ide/components/ide_tree_list.vue';
+import store from '~/ide/stores';
+import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { resetStore, file } from '../helpers';
+import { projectData } from '../mock_data';
+
+describe('IDE tree list', () => {
+ const Component = Vue.extend(IdeTreeList);
+ let vm;
+
+ beforeEach(() => {
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = Object.assign({}, projectData);
+ Vue.set(store.state.trees, 'abcproject/master', {
+ tree: [file('fileName')],
+ loading: false,
+ });
+
+ vm = createComponentWithStore(Component, store, {
+ viewerType: 'edit',
+ });
+
+ spyOn(vm, 'updateViewer').and.callThrough();
+
+ vm.$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ it('updates viewer on mount', () => {
+ expect(vm.updateViewer).toHaveBeenCalledWith('edit');
+ });
+
+ it('renders loading indicator', done => {
+ store.state.trees['abcproject/master'].loading = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.multi-file-loading-container')).not.toBeNull();
+ expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3);
+
+ done();
+ });
+ });
+
+ it('renders list of files', () => {
+ expect(vm.$el.textContent).toContain('fileName');
+ });
+});
diff --git a/spec/javascripts/ide/components/ide_tree_spec.js b/spec/javascripts/ide/components/ide_tree_spec.js
new file mode 100644
index 00000000000..97a0a2432f1
--- /dev/null
+++ b/spec/javascripts/ide/components/ide_tree_spec.js
@@ -0,0 +1,34 @@
+import Vue from 'vue';
+import IdeTree from '~/ide/components/ide_tree.vue';
+import store from '~/ide/stores';
+import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { resetStore, file } from '../helpers';
+import { projectData } from '../mock_data';
+
+describe('IdeRepoTree', () => {
+ let vm;
+
+ beforeEach(() => {
+ const IdeRepoTree = Vue.extend(IdeTree);
+
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = Object.assign({}, projectData);
+ Vue.set(store.state.trees, 'abcproject/master', {
+ tree: [file('fileName')],
+ loading: false,
+ });
+
+ vm = createComponentWithStore(IdeRepoTree, store).$mount();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ it('renders list of files', () => {
+ expect(vm.$el.textContent).toContain('fileName');
+ });
+});
diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js
index 768f6e99bf1..5e3e00a180b 100644
--- a/spec/javascripts/ide/components/repo_commit_section_spec.js
+++ b/spec/javascripts/ide/components/repo_commit_section_spec.js
@@ -1,9 +1,9 @@
import Vue from 'vue';
import store from '~/ide/stores';
import service from '~/ide/services';
+import router from '~/ide/ide_router';
import repoCommitSection from '~/ide/components/repo_commit_section.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import { file, resetStore } from '../helpers';
describe('RepoCommitSection', () => {
@@ -12,10 +12,10 @@ describe('RepoCommitSection', () => {
function createComponent() {
const Component = Vue.extend(repoCommitSection);
- vm = createComponentWithStore(Component, store, {
- noChangesStateSvgPath: 'svg',
- committedStateSvgPath: 'commitsvg',
- });
+ store.state.noChangesStateSvgPath = 'svg';
+ store.state.committedStateSvgPath = 'commitsvg';
+
+ vm = createComponentWithStore(Component, store);
vm.$store.state.currentProjectId = 'abcproject';
vm.$store.state.currentBranchId = 'master';
@@ -60,6 +60,8 @@ describe('RepoCommitSection', () => {
}
beforeEach(done => {
+ spyOn(router, 'push');
+
vm = createComponent();
spyOn(service, 'getTreeData').and.returnValue(
@@ -93,61 +95,49 @@ describe('RepoCommitSection', () => {
resetStore(vm.$store);
const Component = Vue.extend(repoCommitSection);
- vm = createComponentWithStore(Component, store, {
- noChangesStateSvgPath: 'nochangessvg',
- committedStateSvgPath: 'svg',
- }).$mount();
+ store.state.noChangesStateSvgPath = 'nochangessvg';
+ store.state.committedStateSvgPath = 'svg';
- expect(
- vm.$el.querySelector('.js-empty-state').textContent.trim(),
- ).toContain('No changes');
- expect(
- vm.$el.querySelector('.js-empty-state img').getAttribute('src'),
- ).toBe('nochangessvg');
+ vm = createComponentWithStore(Component, store).$mount();
+
+ expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toContain('No changes');
+ expect(vm.$el.querySelector('.js-empty-state img').getAttribute('src')).toBe('nochangessvg');
});
});
it('renders a commit section', () => {
- const changedFileElements = [
- ...vm.$el.querySelectorAll('.multi-file-commit-list li'),
- ];
- const submitCommit = vm.$el.querySelector('form .btn');
- const allFiles = vm.$store.state.changedFiles.concat(
- vm.$store.state.stagedFiles,
- );
+ const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
+ const allFiles = vm.$store.state.changedFiles.concat(vm.$store.state.stagedFiles);
- expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
expect(changedFileElements.length).toEqual(4);
changedFileElements.forEach((changedFile, i) => {
expect(changedFile.textContent.trim()).toContain(allFiles[i].path);
});
-
- expect(submitCommit.disabled).toBeTruthy();
- expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeNull();
});
it('adds changed files into staged files', done => {
- vm.$el.querySelector('.ide-staged-action-btn').click();
-
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.ide-commit-list-container').textContent,
- ).toContain('No changes');
-
- done();
- });
+ vm.$el.querySelector('.multi-file-discard-btn .btn').click();
+ vm
+ .$nextTick()
+ .then(() => vm.$el.querySelector('.multi-file-discard-btn .btn').click())
+ .then(vm.$nextTick)
+ .then(() => {
+ expect(vm.$el.querySelector('.ide-commit-list-container').textContent).toContain(
+ 'No changes',
+ );
+ })
+ .then(done)
+ .catch(done.fail);
});
it('stages a single file', done => {
vm.$el.querySelector('.multi-file-discard-btn .btn').click();
Vue.nextTick(() => {
- expect(
- vm.$el
- .querySelector('.ide-commit-list-container')
- .querySelectorAll('li').length,
- ).toBe(1);
+ expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
+ 1,
+ );
done();
});
@@ -157,26 +147,10 @@ describe('RepoCommitSection', () => {
vm.$el.querySelectorAll('.multi-file-discard-btn .btn')[1].click();
Vue.nextTick(() => {
- expect(
- vm.$el.querySelector('.ide-commit-list-container').textContent,
- ).not.toContain('file1');
- expect(
- vm.$el
- .querySelector('.ide-commit-list-container')
- .querySelectorAll('li').length,
- ).toBe(1);
-
- done();
- });
- });
-
- it('removes all staged files', done => {
- vm.$el.querySelectorAll('.ide-staged-action-btn')[1].click();
-
- Vue.nextTick(() => {
- expect(
- vm.$el.querySelectorAll('.ide-commit-list-container')[1].textContent,
- ).toContain('No changes');
+ expect(vm.$el.querySelector('.ide-commit-list-container').textContent).not.toContain('file1');
+ expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
+ 1,
+ );
done();
});
@@ -190,75 +164,17 @@ describe('RepoCommitSection', () => {
Vue.nextTick(() => {
expect(
- vm.$el
- .querySelectorAll('.ide-commit-list-container')[1]
- .querySelectorAll('li').length,
+ vm.$el.querySelectorAll('.ide-commit-list-container')[1].querySelectorAll('li').length,
).toBe(1);
done();
});
});
- it('updates commitMessage in store on input', done => {
- const textarea = vm.$el.querySelector('textarea');
-
- textarea.value = 'testing commit message';
-
- textarea.dispatchEvent(new Event('input'));
-
- getSetTimeoutPromise()
- .then(() => {
- expect(vm.$store.state.commit.commitMessage).toBe(
- 'testing commit message',
- );
- })
- .then(done)
- .catch(done.fail);
- });
-
- describe('discard draft button', () => {
- it('hidden when commitMessage is empty', () => {
- expect(
- vm.$el.querySelector('.multi-file-commit-form .btn-default'),
- ).toBeNull();
- });
-
- it('resets commitMessage when clicking discard button', done => {
- vm.$store.state.commit.commitMessage = 'testing commit message';
-
- getSetTimeoutPromise()
- .then(() => {
- vm.$el.querySelector('.multi-file-commit-form .btn-default').click();
- })
- .then(Vue.nextTick)
- .then(() => {
- expect(vm.$store.state.commit.commitMessage).not.toBe(
- 'testing commit message',
- );
- })
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('when submitting', () => {
- beforeEach(() => {
- spyOn(vm, 'commitChanges');
- });
-
- it('calls commitChanges', done => {
- vm.$store.state.commit.commitMessage = 'testing commit message';
-
- getSetTimeoutPromise()
- .then(() => {
- vm.$el.querySelector('.multi-file-commit-form .btn-success').click();
- })
- .then(Vue.nextTick)
- .then(() => {
- expect(vm.commitChanges).toHaveBeenCalled();
- })
- .then(done)
- .catch(done.fail);
+ describe('mounted', () => {
+ it('opens last opened file', () => {
+ expect(store.state.openFiles.length).toBe(1);
+ expect(store.state.openFiles[0].pending).toBe(true);
});
});
});
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index b06a6c62a1c..360b6d4dc15 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -5,6 +5,7 @@ import store from '~/ide/stores';
import repoEditor from '~/ide/components/repo_editor.vue';
import monacoLoader from '~/ide/monaco_loader';
import Editor from '~/ide/lib/editor';
+import { activityBarViews } from '~/ide/constants';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
import { file, resetStore } from '../helpers';
@@ -295,4 +296,30 @@ describe('RepoEditor', () => {
});
});
});
+
+ describe('show tabs', () => {
+ it('shows tabs in edit mode', () => {
+ expect(vm.$el.querySelector('.nav-links')).not.toBe(null);
+ });
+
+ it('hides tabs in review mode', done => {
+ vm.$store.state.currentActivityView = activityBarViews.review;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.nav-links')).toBe(null);
+
+ done();
+ });
+ });
+
+ it('hides tabs in commit mode', done => {
+ vm.$store.state.currentActivityView = activityBarViews.commit;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.nav-links')).toBe(null);
+
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/repo_file_spec.js b/spec/javascripts/ide/components/repo_file_spec.js
index 28ff06e1f80..156233653ab 100644
--- a/spec/javascripts/ide/components/repo_file_spec.js
+++ b/spec/javascripts/ide/components/repo_file_spec.js
@@ -73,6 +73,43 @@ describe('RepoFile', () => {
expect(treeChangesEl).not.toBeNull();
expect(treeChangesEl.textContent).toContain('1');
});
+
+ it('renders action dropdown', done => {
+ createComponent({
+ file: {
+ ...file('t4'),
+ type: 'tree',
+ branchId: 'master',
+ projectId: 'project',
+ },
+ level: 0,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.ide-new-btn')).not.toBeNull();
+
+ done();
+ });
+ });
+
+ it('disables action dropdown', done => {
+ createComponent({
+ file: {
+ ...file('t4'),
+ type: 'tree',
+ branchId: 'master',
+ projectId: 'project',
+ },
+ level: 0,
+ disableActionDropdown: true,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.ide-new-btn')).toBeNull();
+
+ done();
+ });
+ });
});
describe('locked file', () => {
diff --git a/spec/javascripts/ide/components/repo_tabs_spec.js b/spec/javascripts/ide/components/repo_tabs_spec.js
index cb785ba2cd3..583f71e6121 100644
--- a/spec/javascripts/ide/components/repo_tabs_spec.js
+++ b/spec/javascripts/ide/components/repo_tabs_spec.js
@@ -26,60 +26,10 @@ describe('RepoTabs', () => {
const tabs = [...vm.$el.querySelectorAll('.multi-file-tab')];
expect(tabs.length).toEqual(2);
- expect(tabs[0].classList.contains('active')).toEqual(true);
- expect(tabs[1].classList.contains('active')).toEqual(false);
+ expect(tabs[0].parentNode.classList.contains('active')).toEqual(true);
+ expect(tabs[1].parentNode.classList.contains('active')).toEqual(false);
done();
});
});
-
- describe('updated', () => {
- it('sets showShadow as true when scroll width is larger than width', done => {
- const el = document.createElement('div');
- el.innerHTML = '<div id="test-app"></div>';
- document.body.appendChild(el);
-
- const style = document.createElement('style');
- style.innerText = `
- .multi-file-tabs {
- width: 100px;
- }
-
- .multi-file-tabs .list-unstyled {
- display: flex;
- overflow-x: auto;
- }
- `;
- document.head.appendChild(style);
-
- vm = createComponent(
- RepoTabs,
- {
- files: [],
- viewer: 'editor',
- hasChanges: false,
- activeFile: file('activeFile'),
- hasMergeRequest: false,
- },
- '#test-app',
- );
-
- vm
- .$nextTick()
- .then(() => {
- expect(vm.showShadow).toEqual(false);
-
- vm.files = openedFiles;
- })
- .then(vm.$nextTick)
- .then(() => {
- expect(vm.showShadow).toEqual(true);
-
- style.remove();
- el.remove();
- })
- .then(done)
- .catch(done.fail);
- });
- });
});
diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js
index 530bdfa2759..b88a12264ca 100644
--- a/spec/javascripts/ide/lib/editor_spec.js
+++ b/spec/javascripts/ide/lib/editor_spec.js
@@ -74,10 +74,10 @@ describe('Multi-file editor library', () => {
scrollBeyondLastLine: false,
quickSuggestions: false,
occurrencesHighlight: false,
- renderLineHighlight: 'none',
- hideCursorInOverviewRuler: true,
wordWrap: 'on',
renderSideBySide: true,
+ renderLineHighlight: 'all',
+ hideCursorInOverviewRuler: false,
});
});
});
diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js
new file mode 100644
index 00000000000..3c6d75ab5e4
--- /dev/null
+++ b/spec/javascripts/ide/mock_data.js
@@ -0,0 +1,15 @@
+// eslint-disable-next-line import/prefer-default-export
+export const projectData = {
+ id: 1,
+ name: 'abcproject',
+ web_url: '',
+ avatar_url: '',
+ path: '',
+ name_with_namespace: 'namespace/abcproject',
+ branches: {
+ master: {
+ treeId: 'abcproject/master',
+ },
+ },
+ mergeRequests: {},
+};
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index 3ee11bd2f03..3ef5a859001 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -511,7 +511,10 @@ describe('IDE store file actions', () => {
actions.stageChange,
'path',
store.state,
- [{ type: types.STAGE_CHANGE, payload: 'path' }],
+ [
+ { type: types.STAGE_CHANGE, payload: 'path' },
+ { type: types.SET_LAST_COMMIT_MSG, payload: '' },
+ ],
[],
done,
);
@@ -524,7 +527,10 @@ describe('IDE store file actions', () => {
actions.unstageChange,
'path',
store.state,
- [{ type: types.UNSTAGE_CHANGE, payload: 'path' }],
+ [
+ { type: types.UNSTAGE_CHANGE, payload: 'path' },
+ { type: types.SET_LAST_COMMIT_MSG, payload: '' },
+ ],
[],
done,
);
@@ -589,20 +595,6 @@ describe('IDE store file actions', () => {
.then(done)
.catch(done.fail);
});
-
- it('returns false when passed in file is active & viewer is diff', done => {
- f.active = true;
- store.state.openFiles.push(f);
- store.state.viewer = 'diff';
-
- store
- .dispatch('openPendingTab', { file: f, keyPrefix: 'pending' })
- .then(added => {
- expect(added).toBe(false);
- })
- .then(done)
- .catch(done.fail);
- });
});
describe('removePendingTab', () => {
diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js
new file mode 100644
index 00000000000..ebd08d95810
--- /dev/null
+++ b/spec/javascripts/ide/stores/actions/project_spec.js
@@ -0,0 +1,71 @@
+import {
+ refreshLastCommitData,
+} from '~/ide/stores/actions';
+import store from '~/ide/stores';
+import service from '~/ide/services';
+import { resetStore } from '../../helpers';
+import testAction from '../../../helpers/vuex_action_helper';
+
+describe('IDE store project actions', () => {
+ beforeEach(() => {
+ store.state.projects.abcproject = {};
+ });
+
+ afterEach(() => {
+ resetStore(store);
+ });
+
+ describe('refreshLastCommitData', () => {
+ beforeEach(() => {
+ store.state.currentProjectId = 'abcproject';
+ store.state.currentBranchId = 'master';
+ store.state.projects.abcproject = {
+ branches: {
+ master: {
+ commit: null,
+ },
+ },
+ };
+ });
+
+ it('calls the service', done => {
+ spyOn(service, 'getBranchData').and.returnValue(
+ Promise.resolve({
+ data: {
+ commit: { id: '123' },
+ },
+ }),
+ );
+
+ store
+ .dispatch('refreshLastCommitData', {
+ projectId: store.state.currentProjectId,
+ branchId: store.state.currentBranchId,
+ })
+ .then(() => {
+ expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('commits getBranchData', done => {
+ testAction(
+ refreshLastCommitData,
+ {},
+ {},
+ [{
+ type: 'SET_BRANCH_COMMIT',
+ payload: {
+ projectId: 'abcproject',
+ branchId: 'master',
+ commit: { id: '123' },
+ },
+ }], // mutations
+ [], // action
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index a64af5b941b..062c3497623 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -2,6 +2,9 @@ import actions, {
stageAllChanges,
unstageAllChanges,
toggleFileFinder,
+ setCurrentBranchId,
+ setEmptyStateSvgs,
+ updateActivityBarView,
updateTempFlagForEntry,
} from '~/ide/stores/actions';
import store from '~/ide/stores';
@@ -306,6 +309,7 @@ describe('Multi-file store actions', () => {
null,
store.state,
[
+ { type: types.SET_LAST_COMMIT_MSG, payload: '' },
{ type: types.STAGE_CHANGE, payload: store.state.changedFiles[0].path },
{ type: types.STAGE_CHANGE, payload: store.state.changedFiles[1].path },
],
@@ -345,6 +349,32 @@ describe('Multi-file store actions', () => {
});
});
+ describe('updateActivityBarView', () => {
+ it('commits UPDATE_ACTIVITY_BAR_VIEW', done => {
+ testAction(
+ updateActivityBarView,
+ 'test',
+ {},
+ [{ type: 'UPDATE_ACTIVITY_BAR_VIEW', payload: 'test' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setEmptyStateSvgs', () => {
+ it('commits setEmptyStateSvgs', done => {
+ testAction(
+ setEmptyStateSvgs,
+ 'svg',
+ {},
+ [{ type: 'SET_EMPTY_STATE_SVGS', payload: 'svg' }],
+ [],
+ done,
+ );
+ });
+ });
+
describe('updateTempFlagForEntry', () => {
it('commits UPDATE_TEMP_FLAG', done => {
const f = {
@@ -388,6 +418,19 @@ describe('Multi-file store actions', () => {
});
});
+ describe('setCurrentBranchId', () => {
+ it('commits setCurrentBranchId', done => {
+ testAction(
+ setCurrentBranchId,
+ 'branchId',
+ {},
+ [{ type: 'SET_CURRENT_BRANCH', payload: 'branchId' }],
+ [],
+ done,
+ );
+ });
+ });
+
describe('toggleFileFinder', () => {
it('commits TOGGLE_FILE_FINDER', done => {
testAction(
diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js
index bd834443730..4833ba3edfd 100644
--- a/spec/javascripts/ide/stores/getters_spec.js
+++ b/spec/javascripts/ide/stores/getters_spec.js
@@ -37,12 +37,6 @@ describe('IDE store getters', () => {
expect(modifiedFiles.length).toBe(1);
expect(modifiedFiles[0].name).toBe('changed');
});
-
- it('returns angle left when collapsed', () => {
- localState.rightPanelCollapsed = true;
-
- expect(getters.collapseButtonIcon(localState)).toBe('angle-double-left');
- });
});
describe('currentMergeRequest', () => {
@@ -147,4 +141,24 @@ describe('IDE store getters', () => {
expect(getters.getChangesInFolder(localState)('test')).toBe(2);
});
});
+
+ describe('lastCommit', () => {
+ it('returns the last commit of the current branch on the current project', () => {
+ const commitTitle = 'Example commit title';
+ const localGetters = {
+ currentProject: {
+ branches: {
+ 'example-branch': {
+ commit: {
+ title: commitTitle,
+ },
+ },
+ },
+ },
+ };
+ localState.currentBranchId = 'example-branch';
+
+ expect(getters.lastCommit(localState, localGetters).title).toBe(commitTitle);
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index b2b4b85ca42..a2869ff378b 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -289,21 +289,6 @@ describe('IDE commit module actions', () => {
.then(done)
.catch(done.fail);
});
-
- it('pushes route to new branch if commitAction is new branch', done => {
- store.state.commit.commitAction = consts.COMMIT_TO_NEW_BRANCH;
-
- store
- .dispatch('commit/updateFilesAfterCommit', {
- data,
- branch,
- })
- .then(() => {
- expect(router.push).toHaveBeenCalledWith(`/project/abcproject/blob/master/${f.path}`);
- })
- .then(done)
- .catch(done.fail);
- });
});
describe('commitChanges', () => {
@@ -391,21 +376,6 @@ describe('IDE commit module actions', () => {
.catch(done.fail);
});
- it('pushes router to new route', done => {
- store
- .dispatch('commit/commitChanges')
- .then(() => {
- expect(router.push).toHaveBeenCalledWith(
- `/project/${store.state.currentProjectId}/blob/${
- store.getters['commit/newBranchName']
- }/changed`,
- );
-
- done();
- })
- .catch(done.fail);
- });
-
it('sets last Commit Msg', done => {
store
.dispatch('commit/commitChanges')
diff --git a/spec/javascripts/ide/stores/mutations/branch_spec.js b/spec/javascripts/ide/stores/mutations/branch_spec.js
index a7167537ef2..29eb859ddaf 100644
--- a/spec/javascripts/ide/stores/mutations/branch_spec.js
+++ b/spec/javascripts/ide/stores/mutations/branch_spec.js
@@ -15,4 +15,26 @@ describe('Multi-file store branch mutations', () => {
expect(localState.currentBranchId).toBe('master');
});
});
+
+ describe('SET_BRANCH_COMMIT', () => {
+ it('sets the last commit on current project', () => {
+ localState.projects = {
+ Example: {
+ branches: {
+ master: {},
+ },
+ },
+ };
+
+ mutations.SET_BRANCH_COMMIT(localState, {
+ projectId: 'Example',
+ branchId: 'master',
+ commit: {
+ title: 'Example commit',
+ },
+ });
+
+ expect(localState.projects.Example.branches.master.commit.title).toBe('Example commit');
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js
index 6fba934810d..e83961fcedc 100644
--- a/spec/javascripts/ide/stores/mutations/file_spec.js
+++ b/spec/javascripts/ide/stores/mutations/file_spec.js
@@ -267,41 +267,23 @@ describe('IDE store file mutations', () => {
it('adds file into openFiles as pending', () => {
mutations.ADD_PENDING_TAB(localState, { file: localFile });
- expect(localState.openFiles.length).toBe(2);
- expect(localState.openFiles[1].pending).toBe(true);
- expect(localState.openFiles[1].key).toBe(`pending-${localFile.key}`);
- });
-
- it('updates open file to pending', () => {
- mutations.ADD_PENDING_TAB(localState, { file: localState.openFiles[0] });
-
expect(localState.openFiles.length).toBe(1);
+ expect(localState.openFiles[0].pending).toBe(true);
+ expect(localState.openFiles[0].key).toBe(`pending-${localFile.key}`);
});
- it('updates pending open file to active', () => {
- localState.openFiles.push({
- ...localFile,
- pending: true,
- });
+ it('only allows 1 open pending file', () => {
+ const newFile = file('test');
+ localState.entries[newFile.path] = newFile;
mutations.ADD_PENDING_TAB(localState, { file: localFile });
- expect(localState.openFiles[1].pending).toBe(true);
- expect(localState.openFiles[1].active).toBe(true);
- });
-
- it('sets all openFiles to not active', () => {
- mutations.ADD_PENDING_TAB(localState, { file: localFile });
+ expect(localState.openFiles.length).toBe(1);
- expect(localState.openFiles.length).toBe(2);
+ mutations.ADD_PENDING_TAB(localState, { file: file('test') });
- localState.openFiles.forEach(f => {
- if (f.pending) {
- expect(f.active).toBe(true);
- } else {
- expect(f.active).toBe(false);
- }
- });
+ expect(localState.openFiles.length).toBe(1);
+ expect(localState.openFiles[0].name).toBe('test');
});
});
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 61efb6372c9..972713c5ad2 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -87,6 +87,28 @@ describe('Multi-file store mutations', () => {
});
});
+ describe('UPDATE_ACTIVITY_BAR_VIEW', () => {
+ it('updates currentActivityBar', () => {
+ mutations.UPDATE_ACTIVITY_BAR_VIEW(localState, 'test');
+
+ expect(localState.currentActivityView).toBe('test');
+ });
+ });
+
+ describe('SET_EMPTY_STATE_SVGS', () => {
+ it('updates empty state SVGs', () => {
+ mutations.SET_EMPTY_STATE_SVGS(localState, {
+ emptyStateSvgPath: 'emptyState',
+ noChangesStateSvgPath: 'noChanges',
+ committedStateSvgPath: 'commited',
+ });
+
+ expect(localState.emptyStateSvgPath).toBe('emptyState');
+ expect(localState.noChangesStateSvgPath).toBe('noChanges');
+ expect(localState.committedStateSvgPath).toBe('commited');
+ });
+ });
+
describe('UPDATE_TEMP_FLAG', () => {
beforeEach(() => {
localState.entries.test = {
diff --git a/spec/javascripts/monitoring/graph/flag_spec.js b/spec/javascripts/monitoring/graph/flag_spec.js
index 2d474e9092f..19278312b6d 100644
--- a/spec/javascripts/monitoring/graph/flag_spec.js
+++ b/spec/javascripts/monitoring/graph/flag_spec.js
@@ -22,15 +22,20 @@ const defaultValuesComponent = {
graphHeightOffset: 120,
showFlagContent: true,
realPixelRatio: 1,
- timeSeries: [{
- values: [{
- time: new Date('2017-06-04T18:17:33.501Z'),
- value: '1.49609375',
- }],
- }],
+ timeSeries: [
+ {
+ values: [
+ {
+ time: new Date('2017-06-04T18:17:33.501Z'),
+ value: '1.49609375',
+ },
+ ],
+ },
+ ],
unitOfDisplay: 'ms',
currentDataIndex: 0,
legendTitle: 'Average',
+ currentCoordinates: [],
};
const deploymentFlagData = {
@@ -113,7 +118,7 @@ describe('GraphFlag', () => {
});
it('formatDate', () => {
- expect(component.formatDate).toEqual('Sun, Jun 4');
+ expect(component.formatDate).toEqual('04 Jun 2017, ');
});
it('cursorStyle', () => {
diff --git a/spec/javascripts/monitoring/graph/track_line_spec.js b/spec/javascripts/monitoring/graph/track_line_spec.js
index 45106830a67..27602a861eb 100644
--- a/spec/javascripts/monitoring/graph/track_line_spec.js
+++ b/spec/javascripts/monitoring/graph/track_line_spec.js
@@ -39,14 +39,14 @@ describe('TrackLine component', () => {
const svgEl = vm.$el.querySelector('svg');
const lineEl = vm.$el.querySelector('svg line');
- expect(svgEl.getAttribute('width')).toEqual('15');
- expect(svgEl.getAttribute('height')).toEqual('6');
+ expect(svgEl.getAttribute('width')).toEqual('16');
+ expect(svgEl.getAttribute('height')).toEqual('8');
expect(lineEl.getAttribute('stroke-width')).toEqual('4');
expect(lineEl.getAttribute('x1')).toEqual('0');
- expect(lineEl.getAttribute('x2')).toEqual('15');
- expect(lineEl.getAttribute('y1')).toEqual('2');
- expect(lineEl.getAttribute('y2')).toEqual('2');
+ expect(lineEl.getAttribute('x2')).toEqual('16');
+ expect(lineEl.getAttribute('y1')).toEqual('4');
+ expect(lineEl.getAttribute('y2')).toEqual('4');
});
});
});
diff --git a/spec/javascripts/monitoring/graph_path_spec.js b/spec/javascripts/monitoring/graph_path_spec.js
index c83bd19345f..2515e2ad897 100644
--- a/spec/javascripts/monitoring/graph_path_spec.js
+++ b/spec/javascripts/monitoring/graph_path_spec.js
@@ -23,6 +23,7 @@ describe('Monitoring Paths', () => {
generatedAreaPath: firstTimeSeries.areaPath,
lineColor: firstTimeSeries.lineColor,
areaColor: firstTimeSeries.areaColor,
+ showDot: false,
});
const metricArea = component.$el.querySelector('.metric-area');
const metricLine = component.$el.querySelector('.metric-line');
@@ -40,6 +41,7 @@ describe('Monitoring Paths', () => {
generatedAreaPath: firstTimeSeries.areaPath,
lineColor: firstTimeSeries.lineColor,
areaColor: firstTimeSeries.areaColor,
+ showDot: false,
});
component.lineStyle = 'dashed';
diff --git a/spec/javascripts/monitoring/graph_spec.js b/spec/javascripts/monitoring/graph_spec.js
index 1213c80ba3a..220228e5c08 100644
--- a/spec/javascripts/monitoring/graph_spec.js
+++ b/spec/javascripts/monitoring/graph_spec.js
@@ -30,7 +30,6 @@ describe('Graph', () => {
it('has a title', () => {
const component = createComponent({
graphData: convertedMetrics[1],
- classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
tagsPath,
@@ -46,7 +45,6 @@ describe('Graph', () => {
it('axisTransform translates an element Y position depending of its height', () => {
const component = createComponent({
graphData: convertedMetrics[1],
- classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
tagsPath,
@@ -62,7 +60,6 @@ describe('Graph', () => {
it('outerViewBox gets a width and height property based on the DOM size of the element', () => {
const component = createComponent({
graphData: convertedMetrics[1],
- classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
tagsPath,
@@ -79,7 +76,6 @@ describe('Graph', () => {
it('sends an event to the eventhub when it has finished resizing', done => {
const component = createComponent({
graphData: convertedMetrics[1],
- classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
tagsPath,
@@ -97,7 +93,6 @@ describe('Graph', () => {
it('has a title for the y-axis and the chart legend that comes from the backend', () => {
const component = createComponent({
graphData: convertedMetrics[1],
- classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
tagsPath,
@@ -111,7 +106,6 @@ describe('Graph', () => {
it('sets the currentData object based on the hovered data index', () => {
const component = createComponent({
graphData: convertedMetrics[1],
- classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
graphIdentifier: 0,
@@ -125,6 +119,5 @@ describe('Graph', () => {
component.positionFlag();
expect(component.currentData).toBe(component.timeSeries[0].values[10]);
- expect(component.currentDataIndex).toEqual(10);
});
});
diff --git a/spec/javascripts/pipelines/graph/action_component_spec.js b/spec/javascripts/pipelines/graph/action_component_spec.js
index 3de10392472..d646bef96f5 100644
--- a/spec/javascripts/pipelines/graph/action_component_spec.js
+++ b/spec/javascripts/pipelines/graph/action_component_spec.js
@@ -22,7 +22,7 @@ describe('pipeline graph action component', () => {
});
it('should emit an event with the provided link', () => {
- eventHub.$on('graphAction', link => {
+ eventHub.$on('postAction', link => {
expect(link).toEqual('foo');
});
});
diff --git a/spec/javascripts/pipelines/mock_data.js b/spec/javascripts/pipelines/mock_data.js
index 59092e0f041..a5a200973d7 100644
--- a/spec/javascripts/pipelines/mock_data.js
+++ b/spec/javascripts/pipelines/mock_data.js
@@ -321,6 +321,103 @@ export const pipelineWithStages = {
};
export const stageReply = {
- html:
- '\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="karma - failed \u0026lt;br\u0026gt; (script failure)" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62402048"\u003e\u003cspan class="ci-status-icon ci-status-icon-failed"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_failed"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003ekarma\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62402048/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="codequality - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398081"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003ecodequality\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398081/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="db:check-schema-pg - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398066"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003edb:check-schema-pg\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398066/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="db:migrate:reset-mysql - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398065"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003edb:migrate:reset-mysql\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398065/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="db:migrate:reset-pg - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398064"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003edb:migrate:reset-pg\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398064/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="db:rollback-mysql - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398070"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003edb:rollback-mysql\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398070/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="db:rollback-pg - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398069"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003edb:rollback-pg\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398069/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="dependency_scanning - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398083"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003edependency_scanning\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398083/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="docs lint - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398061"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003edocs lint\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398061/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="downtime_check - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398062"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003edowntime_check\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398062/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="ee_compat_check - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398063"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003eee_compat_check\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398063/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="gitlab:assets:compile - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398075"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003egitlab:assets:compile\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398075/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="gitlab:setup-mysql - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398073"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003egitlab:setup-mysql\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398073/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="gitlab:setup-pg - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398071"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003egitlab:setup-pg\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398071/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="gitlab_git_test - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398086"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003egitlab_git_test\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398086/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="migration:path-mysql - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398068"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003emigration:path-mysql\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398068/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="migration:path-pg - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398067"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003emigration:path-pg\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398067/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="qa:internal - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398084"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003eqa:internal\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398084/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="qa:selectors - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398085"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003eqa:selectors\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398085/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 0 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398020"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 0 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398020/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 1 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398022"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 1 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398022/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 10 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398033"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 10 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398033/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 11 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398034"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 11 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398034/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 12 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398035"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 12 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398035/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 13 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398036"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 13 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398036/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 14 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398037"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 14 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398037/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 15 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398038"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 15 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398038/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 16 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398039"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 16 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398039/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 17 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398040"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 17 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398040/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 18 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398041"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 18 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398041/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 19 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398042"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 19 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398042/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 2 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398024"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 2 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398024/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 20 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398043"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 20 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398043/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 21 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398044"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 21 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398044/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 22 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398046"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 22 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398046/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 23 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398047"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 23 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398047/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 24 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398048"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 24 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398048/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 25 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398049"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 25 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398049/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 26 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398050"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 26 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398050/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 27 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398051"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 27 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398051/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 3 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398025"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 3 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398025/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 4 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398027"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 4 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398027/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 5 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398028"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 5 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398028/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 6 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398029"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 6 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398029/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 7 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398030"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 7 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398030/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 8 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398031"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 8 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398031/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-mysql 9 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398032"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-mysql 9 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398032/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 0 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397981"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 0 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397981/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 1 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397985"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 1 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397985/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 10 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398000"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 10 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398000/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 11 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398001"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 11 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398001/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 12 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398002"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 12 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398002/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 13 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398003"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 13 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398003/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 14 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398004"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 14 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398004/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 15 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398006"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 15 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398006/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 16 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398007"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 16 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398007/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 17 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398008"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 17 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398008/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 18 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398009"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 18 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398009/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 19 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398010"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 19 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398010/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 2 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397986"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 2 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397986/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 20 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398012"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 20 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398012/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 21 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398013"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 21 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398013/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 22 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398014"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 22 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398014/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 23 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398015"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 23 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398015/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 24 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398016"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 24 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398016/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 25 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398017"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 25 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398017/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 26 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398018"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 26 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398018/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 27 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398019"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 27 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398019/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 3 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397988"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 3 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397988/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 4 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397989"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 4 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397989/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 5 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397991"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 5 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397991/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 6 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397993"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 6 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397993/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 7 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397994"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 7 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397994/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 8 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397995"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 8 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397995/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="rspec-pg 9 28 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62397996"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003erspec-pg 9 28\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62397996/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="sast - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398082"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003esast\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398082/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="spinach-mysql 0 2 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398058"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003espinach-mysql 0 2\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398058/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="spinach-mysql 1 2 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398059"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003espinach-mysql 1 2\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398059/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="spinach-pg 0 2 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398053"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003espinach-pg 0 2\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398053/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="spinach-pg 1 2 - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398056"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003espinach-pg 1 2\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398056/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ca class="mini-pipeline-graph-dropdown-item" data-toggle="tooltip" data-title="static-analysis - passed" data-html="true" data-container="body" href="/gitlab-org/gitlab-ce/-/jobs/62398060"\u003e\u003cspan class="ci-status-icon ci-status-icon-success"\u003e\u003csvg\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#status_success"\u003e\u003c/use\u003e\u003c/svg\u003e\u003c/span\u003e\n\u003cspan class="ci-build-text"\u003estatic-analysis\u003c/span\u003e\n\u003c/a\u003e\u003ca class="ci-action-icon-wrapper js-ci-action-icon" data-toggle="tooltip" data-title="Retry" data-container="body" rel="nofollow" data-method="post" href="/gitlab-org/gitlab-ce/-/jobs/62398060/retry"\u003e\u003csvg class=" icon-action-retry"\u003e\u003cuse xlink:href="https://gitlab.com/assets/icons-fe86f87a3d244c952cc0ec8d7f88c5effefcbe454d751d8449d4a1a32aaaf9a0.svg#retry"\u003e\u003c/use\u003e\u003c/svg\u003e\n\u003c/a\u003e\n\u003c/li\u003e\n',
+ name: 'deploy',
+ title: 'deploy: running',
+ latest_statuses: [
+ {
+ id: 928,
+ name: 'stop staging',
+ started: false,
+ build_path: '/twitter/flight/-/jobs/928',
+ cancel_path: '/twitter/flight/-/jobs/928/cancel',
+ playable: false,
+ created_at: '2018-04-04T20:02:02.728Z',
+ updated_at: '2018-04-04T20:02:02.766Z',
+ status: {
+ icon: 'status_pending',
+ text: 'pending',
+ label: 'pending',
+ group: 'pending',
+ tooltip: 'pending',
+ has_details: true,
+ details_path: '/twitter/flight/-/jobs/928',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_pending-db32e1faf94b9f89530ac519790920d1f18ea8f6af6cd2e0a26cd6840cacf101.ico',
+ action: {
+ icon: 'cancel',
+ title: 'Cancel',
+ path: '/twitter/flight/-/jobs/928/cancel',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 926,
+ name: 'production',
+ started: false,
+ build_path: '/twitter/flight/-/jobs/926',
+ retry_path: '/twitter/flight/-/jobs/926/retry',
+ play_path: '/twitter/flight/-/jobs/926/play',
+ playable: true,
+ created_at: '2018-04-04T20:00:57.202Z',
+ updated_at: '2018-04-04T20:11:13.110Z',
+ status: {
+ icon: 'status_canceled',
+ text: 'canceled',
+ label: 'manual play action',
+ group: 'canceled',
+ tooltip: 'canceled',
+ has_details: true,
+ details_path: '/twitter/flight/-/jobs/926',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_canceled-5491840b9b6feafba0bc599cbd49ee9580321dc809683856cf1b0d51532b1af6.ico',
+ action: {
+ icon: 'play',
+ title: 'Play',
+ path: '/twitter/flight/-/jobs/926/play',
+ method: 'post',
+ },
+ },
+ },
+ {
+ id: 217,
+ name: 'staging',
+ started: '2018-03-07T08:41:46.234Z',
+ build_path: '/twitter/flight/-/jobs/217',
+ retry_path: '/twitter/flight/-/jobs/217/retry',
+ playable: false,
+ created_at: '2018-03-07T14:41:58.093Z',
+ updated_at: '2018-03-07T14:41:58.093Z',
+ status: {
+ icon: 'status_success',
+ text: 'passed',
+ label: 'passed',
+ group: 'success',
+ tooltip: 'passed',
+ has_details: true,
+ details_path: '/twitter/flight/-/jobs/217',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ action: {
+ icon: 'retry',
+ title: 'Retry',
+ path: '/twitter/flight/-/jobs/217/retry',
+ method: 'post',
+ },
+ },
+ },
+ ],
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ tooltip: 'running',
+ has_details: true,
+ details_path: '/twitter/flight/pipelines/13#deploy',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ path: '/twitter/flight/pipelines/13#deploy',
+ dropdown_path: '/twitter/flight/pipelines/13/stage.json?stage=deploy',
};
diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js
index be1632e7206..75156e7bdfd 100644
--- a/spec/javascripts/pipelines/stage_spec.js
+++ b/spec/javascripts/pipelines/stage_spec.js
@@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import stage from '~/pipelines/components/stage.vue';
import eventHub from '~/pipelines/event_hub';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { stageReply } from './mock_data';
describe('Pipelines stage component', () => {
let StageComponent;
@@ -41,7 +42,7 @@ describe('Pipelines stage component', () => {
describe('with successfull request', () => {
beforeEach(() => {
- mock.onGet('path.json').reply(200, { html: 'foo' });
+ mock.onGet('path.json').reply(200, stageReply);
});
it('should render the received data and emit `clickedDropdown` event', done => {
@@ -51,7 +52,7 @@ describe('Pipelines stage component', () => {
setTimeout(() => {
expect(
component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(),
- ).toEqual('foo');
+ ).toContain(stageReply.latest_statuses[0].name);
expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown');
done();
}, 0);
@@ -74,7 +75,9 @@ describe('Pipelines stage component', () => {
describe('update endpoint correctly', () => {
beforeEach(() => {
- mock.onGet('bar.json').reply(200, { html: 'this is the updated content' });
+ const copyStage = Object.assign({}, stageReply);
+ copyStage.latest_statuses[0].name = 'this is the updated content';
+ mock.onGet('bar.json').reply(200, copyStage);
});
it('should update the stage to request the new endpoint provided', done => {
@@ -93,7 +96,7 @@ describe('Pipelines stage component', () => {
setTimeout(() => {
expect(
component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(),
- ).toEqual('this is the updated content');
+ ).toContain('this is the updated content');
done();
});
});
diff --git a/spec/javascripts/projects_dropdown/components/app_spec.js b/spec/javascripts/projects_dropdown/components/app_spec.js
index 2054fef790b..38b31c3d727 100644
--- a/spec/javascripts/projects_dropdown/components/app_spec.js
+++ b/spec/javascripts/projects_dropdown/components/app_spec.js
@@ -23,17 +23,18 @@ const createComponent = () => {
});
};
-const returnServicePromise = (data, failed) => new Promise((resolve, reject) => {
- if (failed) {
- reject(data);
- } else {
- resolve({
- json() {
- return data;
- },
- });
- }
-});
+const returnServicePromise = (data, failed) =>
+ new Promise((resolve, reject) => {
+ if (failed) {
+ reject(data);
+ } else {
+ resolve({
+ json() {
+ return data;
+ },
+ });
+ }
+ });
describe('AppComponent', () => {
describe('computed', () => {
@@ -185,7 +186,7 @@ describe('AppComponent', () => {
describe('fetchSearchedProjects', () => {
const searchQuery = 'test';
- it('should perform search with provided search query', (done) => {
+ it('should perform search with provided search query', done => {
const mockData = [mockRawProject];
spyOn(vm, 'toggleLoader');
spyOn(vm, 'toggleSearchProjectsList');
@@ -203,7 +204,7 @@ describe('AppComponent', () => {
}, 0);
});
- it('should update props for showing search failure', (done) => {
+ it('should update props for showing search failure', done => {
spyOn(vm, 'toggleSearchProjectsList');
spyOn(vm.service, 'getSearchedProjects').and.returnValue(returnServicePromise({}, true));
@@ -219,7 +220,7 @@ describe('AppComponent', () => {
});
describe('logCurrentProjectAccess', () => {
- it('should log current project access via service', (done) => {
+ it('should log current project access via service', done => {
spyOn(vm.service, 'logProjectAccess');
vm.currentProject = mockProject;
@@ -257,7 +258,7 @@ describe('AppComponent', () => {
});
describe('created', () => {
- it('should bind event listeners on eventHub', (done) => {
+ it('should bind event listeners on eventHub', done => {
spyOn(eventHub, '$on');
createComponent().$mount();
@@ -273,7 +274,7 @@ describe('AppComponent', () => {
});
describe('beforeDestroy', () => {
- it('should unbind event listeners on eventHub', (done) => {
+ it('should unbind event listeners on eventHub', done => {
const vm = createComponent();
spyOn(eventHub, '$off');
@@ -305,7 +306,7 @@ describe('AppComponent', () => {
expect(vm.$el.querySelector('.search-input-container')).toBeDefined();
});
- it('should render loading animation', (done) => {
+ it('should render loading animation', done => {
vm.toggleLoader(true);
Vue.nextTick(() => {
const loadingEl = vm.$el.querySelector('.loading-animation');
@@ -317,7 +318,7 @@ describe('AppComponent', () => {
});
});
- it('should render frequent projects list header', (done) => {
+ it('should render frequent projects list header', done => {
vm.toggleFrequentProjectsList(true);
Vue.nextTick(() => {
const sectionHeaderEl = vm.$el.querySelector('.section-header');
@@ -328,7 +329,7 @@ describe('AppComponent', () => {
});
});
- it('should render frequent projects list', (done) => {
+ it('should render frequent projects list', done => {
vm.toggleFrequentProjectsList(true);
Vue.nextTick(() => {
expect(vm.$el.querySelector('.projects-list-frequent-container')).toBeDefined();
@@ -336,7 +337,7 @@ describe('AppComponent', () => {
});
});
- it('should render searched projects list', (done) => {
+ it('should render searched projects list', done => {
vm.toggleSearchProjectsList(true);
Vue.nextTick(() => {
expect(vm.$el.querySelector('.section-header')).toBe(null);
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index c2c92d8ac56..adeea03481f 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -6,6 +6,14 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetMerged', () => {
let vm;
const targetBranch = 'foo';
+ const selectors = {
+ get copyMergeShaButton() {
+ return vm.$el.querySelector('button.js-mr-merged-copy-sha');
+ },
+ get mergeCommitShaLink() {
+ return vm.$el.querySelector('a.js-mr-merged-commit-sha');
+ },
+ };
beforeEach(() => {
const Component = Vue.extend(mergedComponent);
@@ -31,6 +39,9 @@ describe('MRWidgetMerged', () => {
readableClosedAt: '',
},
updatedAt: 'mergedUpdatedAt',
+ shortMergeCommitSha: 'asdf1234',
+ mergeCommitPath: 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d',
+ sourceBranch: 'bar',
targetBranch,
};
@@ -140,6 +151,17 @@ describe('MRWidgetMerged', () => {
expect(vm.$el.textContent).toContain('Cherry-pick');
});
+ it('shows button to copy commit SHA to clipboard', () => {
+ expect(selectors.copyMergeShaButton).toExist();
+ expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(vm.mr.shortMergeCommitSha);
+ });
+
+ it('shows merge commit SHA link', () => {
+ expect(selectors.mergeCommitShaLink).toExist();
+ expect(selectors.mergeCommitShaLink.text).toContain(vm.mr.shortMergeCommitSha);
+ expect(selectors.mergeCommitShaLink.href).toBe(vm.mr.mergeCommitPath);
+ });
+
it('should not show source branch removed text', (done) => {
vm.mr.sourceBranchRemoved = false;
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 3fc7663b9c2..9d2a15ff009 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -18,6 +18,7 @@ export default {
human_total_time_spent: null,
in_progress_merge_commit_sha: null,
merge_commit_sha: '53027d060246c8f47e4a9310fb332aa52f221775',
+ short_merge_commit_sha: '53027d06',
merge_error: null,
merge_params: {
force_remove_source_branch: null,
@@ -215,4 +216,5 @@ export default {
diverged_commits_count: 0,
only_allow_merge_if_pipeline_succeeds: false,
commit_change_content_path: '/root/acets-app/merge_requests/22/commit_change_content',
+ merge_commit_path: 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775',
};