Commit efed14d4 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'winh-pipeline-author-link' into 'master'

Link to commit author user page from pipelines

Closes #29368

See merge request !11100
parents b562b881 50181499
......@@ -62,10 +62,12 @@ export default {
commitAuthor() {
let commitAuthorInformation;
if (!this.pipeline || !this.pipeline.commit) {
return null;
}
// 1. person who is an author of a commit might be a GitLab user
if (this.pipeline &&
this.pipeline.commit &&
this.pipeline.commit.author) {
if (this.pipeline.commit.author) {
// 2. if person who is an author of a commit is a GitLab user
// he/she can have a GitLab avatar
if (this.pipeline.commit.author.avatar_url) {
......@@ -77,11 +79,8 @@ export default {
avatar_url: this.pipeline.commit.author_gravatar_url,
});
}
}
// 4. If committer is not a GitLab User he/she can have a Gravatar
if (this.pipeline &&
this.pipeline.commit) {
// 4. If committer is not a GitLab User he/she can have a Gravatar
} else {
commitAuthorInformation = {
avatar_url: this.pipeline.commit.author_gravatar_url,
web_url: `mailto:${this.pipeline.commit.author_email}`,
......
---
title: Link to commit author user page from pipelines
merge_request: 11100
author:
export default {
id: 73,
user: {
name: 'Administrator',
username: 'root',
id: 1,
state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
web_url: 'http://localhost:3000/root',
},
path: '/root/review-app/pipelines/73',
details: {
status: {
icon: 'icon_status_failed',
text: 'failed',
label: 'failed',
group: 'failed',
has_details: true,
details_path: '/root/review-app/pipelines/73',
},
duration: null,
finished_at: '2017-01-25T00:00:17.130Z',
stages: [{
name: 'build',
title: 'build: failed',
status: {
icon: 'icon_status_failed',
text: 'failed',
label: 'failed',
group: 'failed',
has_details: true,
details_path: '/root/review-app/pipelines/73#build',
},
path: '/root/review-app/pipelines/73#build',
dropdown_path: '/root/review-app/pipelines/73/stage.json?stage=build',
}],
artifacts: [],
manual_actions: [
{
name: 'stop_review',
path: '/root/review-app/builds/1463/play',
},
{
name: 'name',
path: '/root/review-app/builds/1490/play',
},
],
},
flags: {
latest: true,
triggered: false,
stuck: false,
yaml_errors: false,
retryable: true,
cancelable: false,
},
ref:
{
name: 'master',
path: '/root/review-app/tree/master',
tag: false,
branch: true,
},
coverage: '42.21',
commit: {
id: 'fbd79f04fa98717641deaaeb092a4d417237c2e4',
short_id: 'fbd79f04',
title: 'Update .gitlab-ci.yml',
author_name: 'Administrator',
author_email: 'admin@example.com',
created_at: '2017-01-16T12:13:57.000-05:00',
committer_name: 'Administrator',
committer_email: 'admin@example.com',
message: 'Update .gitlab-ci.yml',
author: {
name: 'Administrator',
username: 'root',
id: 1,
state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
web_url: 'http://localhost:3000/root',
},
author_gravatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
commit_url: 'http://localhost:3000/root/review-app/commit/fbd79f04fa98717641deaaeb092a4d417237c2e4',
commit_path: '/root/review-app/commit/fbd79f04fa98717641deaaeb092a4d417237c2e4',
},
retry_path: '/root/review-app/pipelines/73/retry',
created_at: '2017-01-16T17:13:59.800Z',
updated_at: '2017-01-25T00:00:17.132Z',
};
import Vue from 'vue';
import PipelinesTable from '~/commit/pipelines/pipelines_table';
import pipeline from './mock_data';
describe('Pipelines table in Commits and Merge requests', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
let pipeline;
preloadFixtures('static/pipelines_table.html.raw');
preloadFixtures(jsonFixtureName);
beforeEach(() => {
loadFixtures('static/pipelines_table.html.raw');
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
pipeline = pipelines.find(p => p.id === 1);
});
describe('successful request', () => {
......
require 'spec_helper'
describe Projects::PipelinesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'pipelines-project') }
let(:commit) { create(:commit, project: project) }
let(:commit_without_author) { RepoHelpers.another_sample_commit }
let!(:user) { create(:user, email: commit.author_email) }
let!(:pipeline) { create(:ci_pipeline, project: project, sha: commit.id, user: user) }
let!(:pipeline_without_author) { create(:ci_pipeline, project: project, sha: commit_without_author.id) }
let!(:pipeline_without_commit) { create(:ci_pipeline, project: project, sha: '0000') }
render_views
before(:all) do
clean_frontend_fixtures('pipelines/')
end
before(:each) do
sign_in(admin)
end
it 'pipelines/pipelines.json' do |example|
get :index,
namespace_id: namespace,
project_id: project,
format: :json
expect(response).to be_success
store_frontend_fixture(response, example.description)
end
end
export default {
pipelines: [{
id: 115,
user: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
path: '/root/review-app/pipelines/115',
details: {
status: {
icon: 'icon_status_failed',
text: 'failed',
label: 'failed',
group: 'failed',
has_details: true,
details_path: '/root/review-app/pipelines/115',
},
duration: null,
finished_at: '2017-03-17T19:00:15.996Z',
stages: [{
name: 'build',
title: 'build: failed',
status: {
icon: 'icon_status_failed',
text: 'failed',
label: 'failed',
group: 'failed',
has_details: true,
details_path: '/root/review-app/pipelines/115#build',
},
path: '/root/review-app/pipelines/115#build',
dropdown_path: '/root/review-app/pipelines/115/stage.json?stage=build',
},
{
name: 'review',
title: 'review: skipped',
status: {
icon: 'icon_status_skipped',
text: 'skipped',
label: 'skipped',
group: 'skipped',
has_details: true,
details_path: '/root/review-app/pipelines/115#review',
},
path: '/root/review-app/pipelines/115#review',
dropdown_path: '/root/review-app/pipelines/115/stage.json?stage=review',
}],
artifacts: [],
manual_actions: [{
name: 'stop_review',
path: '/root/review-app/builds/3766/play',
}],
},
flags: {
latest: true,
triggered: false,
stuck: false,
yaml_errors: false,
retryable: true,
cancelable: false,
},
ref: {
name: 'thisisabranch',
path: '/root/review-app/tree/thisisabranch',
tag: false,
branch: true,
},
commit: {
id: '9e87f87625b26c42c59a2ee0398f81d20cdfe600',
short_id: '9e87f876',
title: 'Update README.md',
created_at: '2017-03-15T22:58:28.000+00:00',
parent_ids: ['3744f9226e699faec2662a8b267e5d3fd0bfff0e'],
message: 'Update README.md',
author_name: 'Root',
author_email: 'admin@example.com',
authored_date: '2017-03-15T22:58:28.000+00:00',
committer_name: 'Root',
committer_email: 'admin@example.com',
committed_date: '2017-03-15T22:58:28.000+00:00',
author: {
name: 'Root',
username: 'root',
id: 1,
state: 'active',
avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
web_url: 'http://localhost:3000/root',
},
author_gravatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
commit_url: 'http://localhost:3000/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600',
commit_path: '/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600',
},
retry_path: '/root/review-app/pipelines/115/retry',
created_at: '2017-03-15T22:58:33.436Z',
updated_at: '2017-03-17T19:00:15.997Z',
}],
count: {
all: 52,
running: 0,
pending: 0,
finished: 52,
},
};
import Vue from 'vue';
import pipelinesComp from '~/pipelines/pipelines';
import Store from '~/pipelines/stores/pipelines_store';
import pipelinesData from './mock_data';
describe('Pipelines', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
preloadFixtures('static/pipelines.html.raw');
preloadFixtures(jsonFixtureName);
let PipelinesComponent;
let pipeline;
beforeEach(() => {
loadFixtures('static/pipelines.html.raw');
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
pipeline = pipelines.find(p => p.id === 1);
PipelinesComponent = Vue.extend(pipelinesComp);
});
......@@ -17,7 +22,7 @@ describe('Pipelines', () => {
describe('successfull request', () => {
describe('with pipelines', () => {
const pipelinesInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify(pipelinesData), {
next(request.respondWith(JSON.stringify(pipeline), {
status: 200,
}));
};
......
import Vue from 'vue';
import tableRowComp from '~/vue_shared/components/pipelines_table_row';
import pipeline from '../../commit/pipelines/mock_data';
describe('Pipelines Table Row', () => {
let component;
beforeEach(() => {
const jsonFixtureName = 'pipelines/pipelines.json';
const buildComponent = (pipeline) => {
const PipelinesTableRowComponent = Vue.extend(tableRowComp);
component = new PipelinesTableRowComponent({
return new PipelinesTableRowComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
pipeline,
service: {},
},
}).$mount();
};
let component;
let pipeline;
let pipelineWithoutAuthor;
let pipelineWithoutCommit;
preloadFixtures(jsonFixtureName);
beforeEach(() => {
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
pipeline = pipelines.find(p => p.id === 1);
pipelineWithoutAuthor = pipelines.find(p => p.id === 2);
pipelineWithoutCommit = pipelines.find(p => p.id === 3);
});
afterEach(() => {
component.$destroy();
});
it('should render a table row', () => {
component = buildComponent(pipeline);
expect(component.$el).toEqual('TR');
});
describe('status column', () => {
beforeEach(() => {
component = buildComponent(pipeline);
});
it('should render a pipeline link', () => {
expect(
component.$el.querySelector('td.commit-link a').getAttribute('href'),
......@@ -36,6 +56,10 @@ describe('Pipelines Table Row', () => {
});
describe('information column', () => {
beforeEach(() => {
component = buildComponent(pipeline);
});
it('should render a pipeline link', () => {
expect(
component.$el.querySelector('td:nth-child(2) a').getAttribute('href'),
......@@ -63,13 +87,59 @@ describe('Pipelines Table Row', () => {
describe('commit column', () => {
it('should render link to commit', () => {
expect(
component.$el.querySelector('td:nth-child(3) .commit-id').getAttribute('href'),
).toEqual(pipeline.commit.commit_path);
component = buildComponent(pipeline);
const commitLink = component.$el.querySelector('.branch-commit .commit-id');
expect(commitLink.getAttribute('href')).toEqual(pipeline.commit.commit_path);
});
const findElements = () => {
const commitTitleElement = component.$el.querySelector('.branch-commit .commit-title');
const commitAuthorElement = commitTitleElement.querySelector('a.avatar-image-container');
if (!commitAuthorElement) {
return { commitAuthorElement };
}
const commitAuthorLink = commitAuthorElement.getAttribute('href');
const commitAuthorName = commitAuthorElement.querySelector('img.avatar').getAttribute('title');
return { commitAuthorElement, commitAuthorLink, commitAuthorName };
};
it('renders nothing without commit', () => {
expect(pipelineWithoutCommit.commit).toBe(null);
component = buildComponent(pipelineWithoutCommit);
const { commitAuthorElement } = findElements();
expect(commitAuthorElement).toBe(null);
});
it('renders commit author', () => {
component = buildComponent(pipeline);
const { commitAuthorLink, commitAuthorName } = findElements();
expect(commitAuthorLink).toEqual(pipeline.commit.author.web_url);
expect(commitAuthorName).toEqual(pipeline.commit.author.username);
});
it('renders commit with unregistered author', () => {
expect(pipelineWithoutAuthor.commit.author).toBe(null);
component = buildComponent(pipelineWithoutAuthor);
const { commitAuthorLink, commitAuthorName } = findElements();
expect(commitAuthorLink).toEqual(`mailto:${pipelineWithoutAuthor.commit.author_email}`);
expect(commitAuthorName).toEqual(pipelineWithoutAuthor.commit.author_name);
});
});
describe('stages column', () => {
beforeEach(() => {
component = buildComponent(pipeline);
});
it('should render an icon for each stage', () => {
expect(
component.$el.querySelectorAll('td:nth-child(4) .js-builds-dropdown-button').length,
......@@ -78,6 +148,10 @@ describe('Pipelines Table Row', () => {
});
describe('actions column', () => {
beforeEach(() => {
component = buildComponent(pipeline);
});
it('should render the provided actions', () => {
expect(
component.$el.querySelectorAll('td:nth-child(6) ul li').length,
......
import Vue from 'vue';
import pipelinesTableComp from '~/vue_shared/components/pipelines_table';
import '~/lib/utils/datetime_utility';
import pipeline from '../../commit/pipelines/mock_data';
describe('Pipelines Table', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
let pipeline;
let PipelinesTableComponent;
preloadFixtures(jsonFixtureName);
beforeEach(() => {
PipelinesTableComponent = Vue.extend(pipelinesTableComp);
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
pipeline = pipelines.find(p => p.id === 1);
});
describe('table', () => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment