Commit bfb5ebd6 authored by Bryce Johnson's avatar Bryce Johnson

Use help_page_path to generate correct time tracking docs url.

parent 28412f81
//= require lib/utils/pretty_time //= require lib/utils/pretty_time
((app) => { (() => {
const PrettyTime = gl.PrettyTime; const PrettyTime = gl.PrettyTime;
app.collapsedState = { gl.IssuableTimeTrackingApp.collapsedState = {
name: 'time-tracking-collapsed-state', name: 'time-tracking-collapsed-state',
props: [ props: [
'showComparisonState', 'showComparisonState',
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
'showNoTimeTrackingState', 'showNoTimeTrackingState',
'timeSpentHuman', 'timeSpentHuman',
'timeEstimateHuman', 'timeEstimateHuman',
'stopwatchSvg' 'stopwatchSvg',
], ],
methods: { methods: {
abbreviateTime(timeStr) { abbreviateTime(timeStr) {
......
//= require lib/utils/pretty_time //= require lib/utils/pretty_time
((app) => { (() => {
const PrettyTime = gl.PrettyTime; const PrettyTime = gl.PrettyTime;
app.comparisonPane = { gl.IssuableTimeTrackingApp.comparisonPane = {
name: 'time-tracking-comparison-pane', name: 'time-tracking-comparison-pane',
props: [ props: [
'timeSpent', 'timeSpent',
'timeEstimate', 'timeEstimate',
'timeSpentHuman', 'timeSpentHuman',
'timeEstimateHuman' 'timeEstimateHuman',
], ],
computed: { computed: {
parsedRemaining() { parsedRemaining() {
......
((app) => { (() => {
app.estimateOnlyPane = { gl.IssuableTimeTrackingApp.estimateOnlyPane = {
name: 'time-tracking-estimate-only-pane', name: 'time-tracking-estimate-only-pane',
props: ['timeEstimateHuman'], props: ['timeEstimateHuman'],
template: ` template: `
......
((app) => { (() => {
app.helpState = { gl.IssuableTimeTrackingApp.helpState = {
name: 'time-tracking-help-state', name: 'time-tracking-help-state',
data() { props: ['docsUrl'],
return {
docsUrl: '/help/workflow/time_tracking.md',
};
},
template: ` template: `
<div class='time-tracking-help-state'> <div class='time-tracking-help-state'>
<div class='time-tracking-info'> <div class='time-tracking-info'>
......
((app) => { (() => {
app.noTrackingPane = { gl.IssuableTimeTrackingApp.noTrackingPane = {
name: 'time-tracking-no-tracking-pane', name: 'time-tracking-no-tracking-pane',
template: ` template: `
<div class='time-tracking-no-tracking-pane'> <div class='time-tracking-no-tracking-pane'>
......
((app) => { (() => {
app.spentOnlyPane = { gl.IssuableTimeTrackingApp.spentOnlyPane = {
name: 'time-tracking-spent-only-pane', name: 'time-tracking-spent-only-pane',
props: ['timeSpentHuman'], props: ['timeSpentHuman'],
template: ` template: `
......
((app) => { (() => {
const app = gl.IssuableTimeTrackingApp;
gl.IssuableTimeTracker = Vue.component('issuable-time-tracker', { gl.IssuableTimeTracker = Vue.component('issuable-time-tracker', {
name: 'issuable-time-tracker', name: 'issuable-time-tracker',
props: [ props: [
...@@ -6,7 +7,8 @@ ...@@ -6,7 +7,8 @@
'time_spent', 'time_spent',
'human_time_estimate', 'human_time_estimate',
'human_time_spent', 'human_time_spent',
'stopwatchSvg' 'stopwatchSvg',
'docsUrl',
], ],
data() { data() {
return { return {
...@@ -98,7 +100,8 @@ ...@@ -98,7 +100,8 @@
</time-tracking-comparison-pane> </time-tracking-comparison-pane>
<transition name='help-state-toggle'> <transition name='help-state-toggle'>
<time-tracking-help-state <time-tracking-help-state
v-if='showHelpState'> v-if='showHelpState'
:docs-url='docsUrl'>
</time-tracking-help-state> </time-tracking-help-state>
</transition> </transition>
</div> </div>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
//= require smart_interval //= require smart_interval
//= require subbable_resource //= require subbable_resource
((gl) => { (() => {
/* This Vue instance represents what will become the parent instance for the /* This Vue instance represents what will become the parent instance for the
* sidebar. It will be responsible for managing `issuable` state and propagating * sidebar. It will be responsible for managing `issuable` state and propagating
* changes to sidebar components. * changes to sidebar components.
......
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
= dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }}) = dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }})
- if issuable.has_attribute?(:time_estimate) - if issuable.has_attribute?(:time_estimate)
#issuable-time-tracker.block #issuable-time-tracker.block
%issuable-time-tracker{ ':time_estimate' => 'issuable.time_estimate', ':time_spent' => 'issuable.total_time_spent', ':human_time_estimate' => 'issuable.human_time_estimate', ':human_time_spent' => 'issuable.human_total_time_spent', 'stopwatch-svg' => custom_icon('icon_stopwatch') } %issuable-time-tracker{ ':time_estimate' => 'issuable.time_estimate', ':time_spent' => 'issuable.total_time_spent', ':human_time_estimate' => 'issuable.human_time_estimate', ':human_time_spent' => 'issuable.human_total_time_spent', 'stopwatch-svg' => custom_icon('icon_stopwatch'), 'docs-url' => help_page_path('workflow/time_tracking.md')}
// Fallback while content is loading // Fallback while content is loading
.title.hide-collapsed .title.hide-collapsed
Time tracking Time tracking
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
//= require vue //= require vue
//= require issuable/time_tracking/time_tracking_bundle //= require issuable/time_tracking/time_tracking_bundle
function initComponent(time_estimate = 100000, time_spent = 5000, human_time_estimate = '3d 3h 46m', human_time_spent = '1h 23m') { function initComponent(opts = {}) {
fixture.set(` fixture.set(`
<div> <div>
<div id="mock-container"></div> <div id="mock-container"></div>
...@@ -11,19 +11,16 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est ...@@ -11,19 +11,16 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est
`); `);
this.initialData = { this.initialData = {
time_estimate, timeEstimate: opts.timeEstimate || 100000,
time_spent, timeSpent: opts.timeSpent || 5000,
human_time_estimate, timeEstimateHuman: opts.timeEstimateHuman || '3d 3h 46m',
human_time_spent, timeSpentHuman: opts.timeSpentHuman || '1h 23m',
timeEstimateHuman: human_time_estimate, docsUrl: '/help/workflow/time_tracking.md',
timeSpentHuman: human_time_spent,
timeEstimate: time_estimate,
timeSpent: time_spent,
}; };
this.timeTracker = new gl.IssuableTimeTracker({ this.timeTracker = new gl.IssuableTimeTracker({
el: '#mock-container', el: '#mock-container',
propsData: this.initialData data: this.initialData,
}); });
} }
...@@ -38,8 +35,8 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est ...@@ -38,8 +35,8 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est
expect(this.timeTracker).toBeDefined(); expect(this.timeTracker).toBeDefined();
}); });
it ('should correctly set time_estimate', function() { it ('should correctly set timeEstimate', function() {
expect(this.timeTracker.time_estimate).toBe(this.initialData.time_estimate); expect(this.timeTracker.timeEstimate).toBe(this.initialData.timeEstimate);
}); });
it ('should correctly set time_spent', function() { it ('should correctly set time_spent', function() {
expect(this.timeTracker.time_spent).toBe(this.initialData.time_spent); expect(this.timeTracker.time_spent).toBe(this.initialData.time_spent);
...@@ -53,10 +50,13 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est ...@@ -53,10 +50,13 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est
initComponent.apply(this); initComponent.apply(this);
}); });
it('should show the "Comparison" pane when time_estimate and time_spent are truthy', function() { it('should show the "Comparison" pane when timeEstimate and time_spent are truthy', function(done) {
const $comparisonPane = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane'); Vue.nextTick(() => {
expect(this.timeTracker.showComparisonState).toBe(true); const $comparisonPane = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane');
expect($comparisonPane).toBeVisible(); expect(this.timeTracker.showComparisonState).toBe(true);
expect($comparisonPane).toBeVisible();
done();
});
}); });
it('should display the human readable version of time estimated', function() { it('should display the human readable version of time estimated', function() {
...@@ -87,7 +87,7 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est ...@@ -87,7 +87,7 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est
}); });
it('should display the remaining meter with the correct background color when over estimate', function() { it('should display the remaining meter with the correct background color when over estimate', function() {
this.timeTracker.time_estimate = 1; this.timeTracker.timeEstimate = 1;
this.timeTracker.time_spent = 2; this.timeTracker.time_spent = 2;
Vue.nextTick(() => { Vue.nextTick(() => {
const styledMeter = $(this.timeTracker.$el).find('.time-tracking-comparison-pane .over_estimate .meter-fill'); const styledMeter = $(this.timeTracker.$el).find('.time-tracking-comparison-pane .over_estimate .meter-fill');
...@@ -99,112 +99,115 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est ...@@ -99,112 +99,115 @@ function initComponent(time_estimate = 100000, time_spent = 5000, human_time_est
describe("Estimate only pane", function() { describe("Estimate only pane", function() {
beforeEach(function() { beforeEach(function() {
initComponent.apply(this, [10000, 0, '2h 46m', 0]); initComponent.apply(this, { timeEstimate: 10000, timeSpent: '0', timeEstimateHuman: '2h 46m', timeSpentHuman: '0' });
}); });
it('should only show the "Estimate only" pane when time_estimate is truthy and time_spent is falsey', function() { it('should only show the "Estimate only" pane when timeEstimate is truthy and time_spent is falsey', function() {
const $estimateOnlyPane = this.timeTracker.$el.querySelector('.time-tracking-estimate-only-pane'); Vue.nextTick(() => {
const $estimateOnlyPane = this.timeTracker.$el.querySelector('.time-tracking-estimate-only-pane');
expect(this.timeTracker.showEstimateOnlyState).toBe(true); expect(this.timeTracker.showEstimateOnlyState).toBe(true);
expect($estimateOnlyPane).toBeVisible(); expect($estimateOnlyPane).toBeVisible();
});
}); });
it('should display the human readable version of time estimated', function() { it('should display the human readable version of time estimated', function() {
const estimateText = this.timeTracker.$el.querySelector('.time-tracking-estimate-only-pane').innerText; Vue.nextTick(() => {
const correctText = 'Estimated: 2h 46m'; const estimateText = this.timeTracker.$el.querySelector('.time-tracking-estimate-only-pane').innerText;
const correctText = 'Estimated: 2h 46m';
expect(estimateText).toBe(correctText); expect(estimateText).toBe(correctText);
});
}); });
}); });
describe('Spent only pane', function() { describe('Spent only pane', function() {
beforeEach(function() { beforeEach(function() {
initComponent.apply(this, [0, 5000]); initComponent.apply(this, { timeEstimate: 0, timeSpent: 5000 });
}); });
// Look for the value // Look for the value
it('should only show the "Spent only" pane when time_estimate is falsey and time_spent is truthy', function() { it('should only show the "Spent only" pane when timeEstimate is falsey and time_spent is truthy', function() {
const $spentOnlyPane = this.timeTracker.$el.querySelector('.time-tracking-spend-only-pane'); Vue.nextTick(() => {
const $spentOnlyPane = this.timeTracker.$el.querySelector('.time-tracking-spend-only-pane');
expect(this.timeTracker.showSpentOnlyState).toBe(true); expect(this.timeTracker.showSpentOnlyState).toBe(true);
expect($spentOnlyPane).toBeVisible(); expect($spentOnlyPane).toBeVisible();
});
}); });
it('should display the human readable version of time spent', function() { it('should display the human readable version of time spent', function() {
const spentText = this.timeTracker.$el.querySelector('.time-tracking-spend-only-pane').innerText; Vue.nextTick(() => {
const correctText = 'Spent: 1h 23m'; const spentText = this.timeTracker.$el.querySelector('.time-tracking-spend-only-pane').innerText;
const correctText = 'Spent: 1h 23m';
expect(spentText).toBe(correctText); expect(spentText).toBe(correctText);
});
}); });
}); });
describe('No time tracking pane', function() { describe('No time tracking pane', function() {
beforeEach(function() { beforeEach(function() {
initComponent.apply(this, [0, 0, 0, 0]); initComponent.apply(this, { timeEstimate: 0, timeSpent: 0, timeEstimateHuman: 0, timeSpentHuman: 0 });
}); });
it('should only show the "No time tracking" pane when both time_estimate and time_spent are falsey', function() { it('should only show the "No time tracking" pane when both timeEstimate and time_spent are falsey', function() {
const $noTrackingPane = this.timeTracker.$el.querySelector('.time-tracking-no-tracking-pane'); Vue.nextTick(() => {
const noTrackingText =$noTrackingPane.innerText; const $noTrackingPane = this.timeTracker.$el.querySelector('.time-tracking-no-tracking-pane');
const correctText = 'No estimate or time spent'; const noTrackingText =$noTrackingPane.innerText;
const correctText = 'No estimate or time spent';
expect(this.timeTracker.showNoTimeTrackingState).toBe(true); expect(this.timeTracker.showNoTimeTrackingState).toBe(true);
expect($noTrackingPane).toBeVisible(); expect($noTrackingPane).toBeVisible();
expect(noTrackingText).toBe(correctText); expect(noTrackingText).toBe(correctText);
});
}); });
}); });
describe("Help pane", function() { describe("Help pane", function() {
beforeEach(function() { beforeEach(function() {
initComponent.apply(this, [0, 0]); initComponent.apply(this, { timeEstimate: 0, timeSpent: 0 });
}); });
it('should not show the "Help" pane by default', function() { it('should not show the "Help" pane by default', function() {
const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
expect(this.timeTracker.showHelpState).toBe(false);
expect($helpPane).toBeNull();
});
it('should link to the correct documentation', function(done) {
const correctUrl = '/help/workflow/time_tracking.md'
$(this.timeTracker.$el).find('.help-button').click();
Vue.nextTick(() => { Vue.nextTick(() => {
const currentHref = $(this.timeTracker.$el).find('.learn-more-button').attr('href'); const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
expect(currentHref).toBe(correctUrl);
done();
});
expect(this.timeTracker.showHelpState).toBe(false);
expect($helpPane).toBeNull();
});
}); });
it('should show the "Help" pane when help button is clicked', function(done) { it('should show the "Help" pane when help button is clicked', function(done) {
$(this.timeTracker.$el).find('.help-button').click();
Vue.nextTick(() => { Vue.nextTick(() => {
const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state'); $(this.timeTracker.$el).find('.help-button').click();
expect(this.timeTracker.showHelpState).toBe(true);
expect($helpPane).toBeVisible(); setTimeout(() => {
done(); const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
expect(this.timeTracker.showHelpState).toBe(true);
expect($helpPane).toBeVisible();
done();
}, 100);
}); });
}); });
it('should not show the "Help" pane when help button is clicked and then closed', function(done) { it('should not show the "Help" pane when help button is clicked and then closed', function(done) {
$(this.timeTracker.$el).find('.help-button').click(); Vue.nextTick(() => {
$(this.timeTracker.$el).find('.help-button').click();
setTimeout(() => { setTimeout(() => {
$(this.timeTracker.$el).find('.close-help-button').click(); $(this.timeTracker.$el).find('.close-help-button').click();
setTimeout(() => { setTimeout(() => {
const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state'); const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
expect(this.timeTracker.showHelpState).toBe(false); expect(this.timeTracker.showHelpState).toBe(false);
expect($helpPane).toBeNull(); expect($helpPane).toBeNull();
done(); done();
}, 1000);
}, 1000); }, 1000);
}, 1000); });
}); });
}); });
}); });
......
...@@ -65,6 +65,14 @@ shared_examples 'issuable time tracker' do ...@@ -65,6 +65,14 @@ shared_examples 'issuable time tracker' do
expect(page).not_to have_content 'Learn more' expect(page).not_to have_content 'Learn more'
end end
end end
it 'displays the correct help url' do
page.within '#issuable-time-tracker' do
find('.help-button').click
expect(find_link('Learn more')[:href]).to eq('/help/workflow/time_tracking.md')
end
end
end end
def submit_time(slash_command) def submit_time(slash_command)
......
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