trajectory 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +19 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +57 -0
  9. data/Rakefile +18 -0
  10. data/config/env.yml +3 -0
  11. data/lib/trajectory/client.rb +24 -0
  12. data/lib/trajectory/core_ext/hash.rb +8 -0
  13. data/lib/trajectory/core_ext.rb +1 -0
  14. data/lib/trajectory/data_access/api.rb +54 -0
  15. data/lib/trajectory/data_access/data_store.rb +70 -0
  16. data/lib/trajectory/data_access.rb +2 -0
  17. data/lib/trajectory/domain/idea.rb +70 -0
  18. data/lib/trajectory/domain/ideas.rb +23 -0
  19. data/lib/trajectory/domain/iteration.rb +85 -0
  20. data/lib/trajectory/domain/iterations.rb +46 -0
  21. data/lib/trajectory/domain/project.rb +168 -0
  22. data/lib/trajectory/domain/projects.rb +55 -0
  23. data/lib/trajectory/domain/stories.rb +56 -0
  24. data/lib/trajectory/domain/story.rb +119 -0
  25. data/lib/trajectory/domain/update.rb +17 -0
  26. data/lib/trajectory/domain/user.rb +28 -0
  27. data/lib/trajectory/domain/users.rb +30 -0
  28. data/lib/trajectory/domain.rb +11 -0
  29. data/lib/trajectory/exceptions/bad_environment_error.rb +7 -0
  30. data/lib/trajectory/exceptions/missing_attribute_error.rb +12 -0
  31. data/lib/trajectory/exceptions/velocity_equal_to_zero_error.rb +11 -0
  32. data/lib/trajectory/exceptions.rb +3 -0
  33. data/lib/trajectory/version.rb +3 -0
  34. data/lib/trajectory.rb +15 -0
  35. data/spec/fabricators/iteration_fabricator.rb +18 -0
  36. data/spec/fabricators/project_fabricator.rb +11 -0
  37. data/spec/fabricators/story_fabricator.rb +18 -0
  38. data/spec/fixtures/vcr_cassettes/client.yml +853 -0
  39. data/spec/fixtures/vcr_cassettes/projects_and_ideas.yml +98 -0
  40. data/spec/fixtures/vcr_cassettes/projects_and_ideas_association.yml +52 -0
  41. data/spec/fixtures/vcr_cassettes/projects_and_iterations.yml +49 -0
  42. data/spec/fixtures/vcr_cassettes/projects_and_iterations_association.yml +49 -0
  43. data/spec/fixtures/vcr_cassettes/projects_and_stories.yml +101 -0
  44. data/spec/fixtures/vcr_cassettes/projects_and_stories_association.yml +147 -0
  45. data/spec/integration/client_spec.rb +96 -0
  46. data/spec/spec_helper.rb +13 -0
  47. data/spec/support/env.rb +11 -0
  48. data/spec/support/fabrication.rb +1 -0
  49. data/spec/support/simple_cov.rb +11 -0
  50. data/spec/support/timecop.rb +1 -0
  51. data/spec/support/vcr.rb +8 -0
  52. data/spec/unit/client_spec.rb +27 -0
  53. data/spec/unit/core_ext/hash_spec.rb +11 -0
  54. data/spec/unit/data_access/api_spec.rb +100 -0
  55. data/spec/unit/data_access/data_store_spec.rb +86 -0
  56. data/spec/unit/domain/idea_spec.rb +46 -0
  57. data/spec/unit/domain/ideas_spec.rb +22 -0
  58. data/spec/unit/domain/iteration_spec.rb +51 -0
  59. data/spec/unit/domain/iterations_spec.rb +47 -0
  60. data/spec/unit/domain/project_spec.rb +163 -0
  61. data/spec/unit/domain/projects_spec.rb +84 -0
  62. data/spec/unit/domain/stories_spec.rb +84 -0
  63. data/spec/unit/domain/story_spec.rb +71 -0
  64. data/spec/unit/domain/user_spec.rb +29 -0
  65. data/spec/unit/domain/users_spec.rb +36 -0
  66. data/spec/unit/exceptions/bad_environment_error_spec.rb +9 -0
  67. data/spec/unit/exceptions/missing_attribute_error_spec.rb +10 -0
  68. data/spec/unit/exceptions/velocity_equal_to_zero_error_spec.rb +10 -0
  69. data/trajectory.gemspec +21 -0
  70. metadata +174 -0
@@ -0,0 +1,168 @@
1
+ module Trajectory
2
+ class Project
3
+ include Virtus
4
+
5
+ NUMBER_OF_WORKING_DAYS_BY_WEEK = 5.0
6
+
7
+ attr_writer :stories, :users_collection
8
+
9
+ # @return [Integer] the unique identifier of the project.
10
+ # @raise [MissingAttributeError] if id is nil
11
+ attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
12
+
13
+ # @return [String] the project name
14
+ attribute :name, String
15
+ # @!method archived?
16
+ # @return [true, false] true if the project has been archived, false otherwise
17
+ attribute :archived, Boolean
18
+ # @return [DateTime] creation date of the project
19
+ attribute :created_at, DateTime
20
+ # @return [Integer] the current velocity of the project
21
+ attribute :estimated_velocity, Integer
22
+ # @return [Array<Integer>] the velocities of past iterations
23
+ attribute :historic_velocity, Array[Integer]
24
+ # @return [String] project keyword identifier
25
+ attribute :keyword, String
26
+ # @return [DateTime] last modification date of the project
27
+ attribute :updated_at, DateTime
28
+ # @return [Integer] number of completed iterations in the project
29
+ attribute :completed_iterations_count, Integer
30
+ # @return [Integer] number of completed stories in the project
31
+ attribute :completed_stories_count, Integer
32
+
33
+ # Returns true if two projects are the sames i.e they share the same id
34
+ # attribute
35
+ #
36
+ # @param other [Project] the other object to compare
37
+ # @return [true, false]
38
+ def ==(other)
39
+ id == other.id
40
+ end
41
+
42
+ # Fetch all stories that belongs to the project
43
+ #
44
+ # @return [Stories] the stories collection
45
+ def stories
46
+ @stories ||= DataStore.stories_for_project(self)
47
+ end
48
+
49
+ # Fetch all iterations that belongs to the project
50
+ #
51
+ # @return [Iterations] the iterations collection
52
+ def iterations
53
+ @iterations ||= DataStore.iterations_for_project(self)
54
+ end
55
+
56
+ # Fetch all ideas that belongs to the project
57
+ #
58
+ # @return [Ideas] the ideas collection
59
+ def ideas
60
+ @ideas ||= DataStore.ideas_for_project(self)
61
+ end
62
+
63
+ # Fetch all users that belongs to the project
64
+ #
65
+ # @return [Users] the users collection
66
+ def users
67
+ @users_collection ||= DataStore.users_for_project(self)
68
+ end
69
+
70
+ # Fetch updates that belongs to the project since a given date
71
+ #
72
+ # @param since [DateTime] the date
73
+ # @return [Updates] the updates collection
74
+ def updates(since = DateTime.now)
75
+ DataStore.updates_for_project(self, since)
76
+ end
77
+
78
+ # Fetch a user from the project given its id or false if it does not exist
79
+ #
80
+ # @return [User, false] the user or false
81
+ def find_user_by_id(id)
82
+ users.find_by_id(id)
83
+ end
84
+
85
+ # Fetch the stories in a given iteration of a project
86
+ #
87
+ # @param iteration [Iteration] the iteration
88
+ # @return [Stories] the user
89
+ def stories_in_iteration(iteration)
90
+ stories.in_iteration(iteration)
91
+ end
92
+
93
+ # Returns the sum of all points of each story of the project
94
+ #
95
+ # @return [Integer] the points accumulation
96
+ def total_points
97
+ stories.inject(0) do |accumulator, story|
98
+ accumulator += story.points
99
+ end
100
+ end
101
+
102
+ # Returns estimated end date of the project with the actual estimated velocity
103
+ #
104
+ # @return [Date] the estimated date
105
+ def estimated_end_date
106
+ Date.today + remaining_days
107
+ end
108
+
109
+ # Returns the estimated number of days (weekend included) remaining before the end of the project.
110
+ #
111
+ # This is usefull to estimate the project end date.
112
+ #
113
+ # @return [Integer] the number of days
114
+ # @raise [VelocityEqualToZeroError] if estimated velocity is equal to zero (i.e the project can't be finished because no one actually works on it)
115
+ def remaining_days
116
+ raise VelocityEqualToZeroError.new(self) if estimated_velocity_per_day == 0
117
+ (remaining_points / estimated_velocity_per_day).ceil
118
+ end
119
+
120
+ # Returns sum of points of not completed stories
121
+ #
122
+ # @return [Integer] the number of points
123
+ def remaining_points
124
+ stories.not_completed.inject(0) do |accumulator, story|
125
+ accumulator += story.points
126
+ end
127
+ end
128
+
129
+ # Returns the estimated velocity by day over a week
130
+ #
131
+ # @return [Integer] the estimated velocity
132
+ def estimated_velocity_per_day
133
+ estimated_velocity / 7.0
134
+ end
135
+
136
+ # Returns the estimated number of working days (weekend excluded) remaining before the end of the project.
137
+ #
138
+ # This is usefull to be able to evaluate project budget as not all days are billable.
139
+ #
140
+ # @return [Integer] the number of days
141
+ # @raise [VelocityEqualToZeroError] if estimated velocity is equal to zero (i.e the project can't be finished because no one actually works on it)
142
+ def remaining_working_days
143
+ raise VelocityEqualToZeroError.new(self) if estimated_velocity_per_working_day == 0
144
+ (remaining_points / estimated_velocity_per_working_day).ceil
145
+ end
146
+
147
+ # Returns the estimated velocity by day over billable days (actually 5 days)
148
+ #
149
+ # @return [Integer] the estimated velocity
150
+ def estimated_velocity_per_working_day
151
+ estimated_velocity / NUMBER_OF_WORKING_DAYS_BY_WEEK
152
+ end
153
+
154
+ # Returns the completion percentage of the project
155
+ #
156
+ # @return [Float] the percentage
157
+ def percent_complete
158
+ (accepted_points.to_f / total_points * 100.0).round(1)
159
+ end
160
+
161
+ # Returns the sum of accepted story points
162
+ #
163
+ # @return [Integer] the number of points
164
+ def accepted_points
165
+ total_points - remaining_points
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,55 @@
1
+ require 'delegate'
2
+
3
+ module Trajectory
4
+ class Projects < SimpleDelegator
5
+ alias :projects :__getobj__
6
+
7
+ # Creates a new collection of {Projects}
8
+ #
9
+ # @param projects [Array<Project>] a arbitrary lenght list of {Project} objects
10
+ def initialize(*projects)
11
+ super(projects)
12
+ end
13
+
14
+ # Create a new collection of {Project} from a JSON array of attributes from trajectory API
15
+ #
16
+ # @param json_attributes [Hash] the hash of attributes of each project of the collection
17
+ def self.from_json(json_attributes)
18
+ new(*json_attributes.map do |attributes|
19
+ Project.new(attributes.symbolize_keys!)
20
+ end)
21
+ end
22
+
23
+ # Fetch the project with the given id in the collection. If it is not found,
24
+ # it returns false
25
+ #
26
+ # @param id [Integer] the project id
27
+ # @return [Project, false] the found project or false
28
+ def find_by_id(id)
29
+ projects.find { |project| project.id == id } || false
30
+ end
31
+
32
+ # Fetch the project with the given keyword in the collection. If it is not found,
33
+ # it returns false
34
+ #
35
+ # @param keyword [String] the project keyword
36
+ # @return [Project, false] the found project or false
37
+ def find_by_keyword(keyword)
38
+ projects.find { |project| project.keyword == keyword } || false
39
+ end
40
+
41
+ # Returns the archived projects of the collection
42
+ #
43
+ # @return [Projects] the filtered collection
44
+ def archived
45
+ projects.select { |project| project.archived? }
46
+ end
47
+
48
+ # Returns the active projects of the collection
49
+ #
50
+ # @return [Projects] the filtered collection
51
+ def active
52
+ projects.select { |project| !project.archived? }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,56 @@
1
+ require 'delegate'
2
+
3
+ module Trajectory
4
+ class Stories < SimpleDelegator
5
+ alias :stories :__getobj__
6
+
7
+ # Creates a new collection of {Story}
8
+ #
9
+ # @param stories [Array<Story>] a arbitrary lenght list of {Story} objects
10
+ def initialize(*stories)
11
+ super(stories)
12
+ end
13
+
14
+ # Create a new collection of {Story} from a JSON array of attributes from trajectory API
15
+ #
16
+ # @param project [Project] the project the stories belongs to
17
+ # @param json_attributes [Hash] the hash of attributes of each story of the collection
18
+ def self.from_json(project, json_attributes)
19
+ new(*json_attributes.map do |attributes|
20
+ attributes = attributes.symbolize_keys!.merge({project_id: project.id})
21
+ Story.new(attributes)
22
+ end)
23
+ end
24
+
25
+ # Returns started stories of the collection
26
+ #
27
+ # @return [Stories] started stories collection
28
+ def started
29
+ stories.select(&:started?)
30
+ end
31
+
32
+ # Returns unstarted stories of the collection
33
+ #
34
+ # @return [Stories] unstarted stories collection
35
+ def unstarted
36
+ stories.select(&:unstarted?)
37
+ end
38
+
39
+ # Returns not completed stories of the collection
40
+ #
41
+ # @return [Stories] not completed stories collection
42
+ def not_completed
43
+ stories.reject(&:completed?)
44
+ end
45
+
46
+ # Returns stories of the collection that are in the given iteration
47
+ #
48
+ # @param iteration [Iteration] an iteration
49
+ # @return [Stories] stories collection in iteration
50
+ def in_iteration(iteration)
51
+ stories.select do |story|
52
+ story.in_iteration?(iteration)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,119 @@
1
+ module Trajectory
2
+ class Story
3
+ include Virtus
4
+
5
+ # @return [Integer] the unique identifier of the story
6
+ # @raise [MissingAttributeError] if id is nil
7
+ attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
8
+ # @return [String] the name of the user assigned to the story
9
+ attribute :assignee_name, String
10
+ # @return [String] the type of story as "Feature", "Bug", "Todo" or "Milestone"
11
+ attribute :task_type, String
12
+ # The Integer position of the story in the backlog.
13
+ # Lower is higher.
14
+ # @return [Integer] the position of the story
15
+ attribute :position, Integer
16
+ # @return [DateTime] the creation date of the story
17
+ attribute :created_at, DateTime
18
+ # @return [Array<String>] the valid states the story can transition to
19
+ # @todo Replace String by Symbol
20
+ attribute :state_events, Array[String]
21
+ # @return [String] the title of the story
22
+ attribute :title, String
23
+ # @return [true, false] true if design is needed for the story, false otherwise
24
+ attribute :design_needed, Boolean
25
+ # @return [DateTime] the last modification date of the story
26
+ attribute :updated_at, DateTime
27
+ # @return [String] the subject of the idea the story is attached to
28
+ attribute :idea_subject, String
29
+ # @!method archived?
30
+ # @return [true, false] true if the story has been archived, false otherwise
31
+ attribute :archived, Boolean
32
+ # @return [Integer] estimation in points of the story complexity
33
+ attribute :points, Integer
34
+ # @!method development_needed?
35
+ # @return [true, false] true if development is needed for the story, false otherwise
36
+ attribute :development_needed, Boolean
37
+ # @!method deleted?
38
+ # @return [ture, false] true if the story has been deleted, false otherwise
39
+ attribute :deleted, Boolean
40
+ # @return [String] name of the user that created the story
41
+ attribute :user_name, String
42
+ # @return [Integer] id of the user that created the story
43
+ # @see #user
44
+ attribute :user_id, Integer
45
+ # @return [Integer] number of comments of the story
46
+ attribute :comments_count, Integer
47
+ # @return [Symbol] state of the story in [:started, :unstarted, :delivered, :accepted, :rejected]
48
+ attribute :state, Symbol
49
+ # @return [Integer] project id the story belongs to
50
+ # @see #project
51
+ attribute :project_id, Integer
52
+ # @return [Integer] iteration id the story belongs to
53
+ # @see #iteration
54
+ attribute :iteration_id, Integer
55
+
56
+ # Returns true if two stories are the sames i.e they share the same id
57
+ # attribute
58
+ #
59
+ # @param other [Story] the other object to compare
60
+ # @return [true, false]
61
+ def ==(other)
62
+ id == other.id
63
+ end
64
+
65
+ # Fetch the project the story belongs to
66
+ #
67
+ # @return [Project]
68
+ def project
69
+ @project ||= DataStore.find_project_by_id(project_id)
70
+ end
71
+
72
+ # Returns true if the story is started i.e in :started state
73
+ #
74
+ # @return [true, false]
75
+ def started?
76
+ state == :started
77
+ end
78
+
79
+ # Returns true if the story is not started i.e in :unstarted state
80
+ #
81
+ # @return [true, false]
82
+ def unstarted?
83
+ state == :unstarted
84
+ end
85
+
86
+ # Returns true if the story is not completed i.e not in :accepted state
87
+ #
88
+ # @return [true, false]
89
+ def not_completed?
90
+ !completed?
91
+ end
92
+
93
+ # Returns true if the story is completed i.e in :accepted state
94
+ #
95
+ # @return [true, false]
96
+ def completed?
97
+ state == :accepted
98
+ end
99
+
100
+ # Fetch the user that created the story
101
+ #
102
+ # @return [User]
103
+ def user
104
+ @user ||= DataStore.find_user_of_project_with_id(project, user_id)
105
+ end
106
+
107
+ # Returns true if the story belongs to the given iteration, false otherwise
108
+ #
109
+ # @param iteration [Iteration] an iteration
110
+ # @return [true, false]
111
+ def in_iteration?(iteration)
112
+ iteration_id == iteration.id
113
+ end
114
+
115
+ # @todo Not Yet Implemented
116
+ def iteration
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,17 @@
1
+ module Trajectory
2
+ class Update
3
+ # @attr_reader stories [Stories] the updated stories by the update
4
+ # @attr_reader iterations [iterations] the updated iterations by the update
5
+ attr_reader :stories, :iterations
6
+
7
+ # Creates a new update with given updated stories and iterations
8
+ #
9
+ # @param stories [Stories] collection of updated stories
10
+ # @param iterations [Iterations] collection of updated iterations
11
+ # :nocov:
12
+ def initialize(stories, iterations)
13
+ @stories = stories
14
+ @iterations = iterations
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,28 @@
1
+ module Trajectory
2
+ class User
3
+ include Virtus
4
+
5
+ # @return [Integer] the unique identifier of the User
6
+ # @raise [MissingAttributeError] if id is nil
7
+ attribute :id, Integer, default: lambda { |project, attribute| raise MissingAttributeError.new(project, :id) }
8
+ # @return [String] the full name of the user
9
+ attribute :name, String
10
+ # @return [DateTime] the creation date of the user account
11
+ attribute :created_at, DateTime
12
+ # @return [DateTime] the last modification date of the user account
13
+ attribute :updated_at, DateTime
14
+ # @return [String] the url to the avatar image of the user (uses gravatar)
15
+ attribute :gravatar_url, String
16
+ # @return [String] the email of the user
17
+ attribute :email, String
18
+
19
+ # Returns true if two users are the sames i.e they share the same id
20
+ # attribute
21
+ #
22
+ # @param other [User] the other object to compare
23
+ # @return [true, false]
24
+ def ==(other)
25
+ id == other.id
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ module Trajectory
2
+ class Users < SimpleDelegator
3
+ alias :users :__getobj__
4
+
5
+ # Creates a new collection of {User}
6
+ #
7
+ # @param users [Array<User>] a arbitrary lenght list of {User} objects
8
+ def initialize(*users)
9
+ super(users)
10
+ end
11
+
12
+ # Create a new collection of {User} from a JSON array of attributes from trajectory API
13
+ #
14
+ # @param json_attributes [Hash] the hash of attributes of each user of the collection
15
+ def self.from_json(json_attributes)
16
+ new(*json_attributes.map do |attributes|
17
+ User.new(attributes.symbolize_keys!)
18
+ end)
19
+ end
20
+
21
+ # Returns the the first user with the given id in the collection or false if
22
+ # no user can be found with the id
23
+ #
24
+ # @param id [Integer] the id of the user to find
25
+ # @return [User, false] the user with the given id
26
+ def find_by_id(id)
27
+ users.find { |user| user.id == id } || false
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ require 'trajectory/domain/story'
2
+ require 'trajectory/domain/stories'
3
+ require 'trajectory/domain/project'
4
+ require 'trajectory/domain/projects'
5
+ require 'trajectory/domain/iteration'
6
+ require 'trajectory/domain/iterations'
7
+ require 'trajectory/domain/idea'
8
+ require 'trajectory/domain/ideas'
9
+ require 'trajectory/domain/user'
10
+ require 'trajectory/domain/users'
11
+ require 'trajectory/domain/update'
@@ -0,0 +1,7 @@
1
+ module Trajectory
2
+ class BadEvnrionmentError < RuntimeError
3
+ def to_s
4
+ "Specify trajectory API environment variables : TRAJECTORY_API_KEY and TRAJECTORY_ACCOUNT_KEYWORD"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Trajectory
2
+ class MissingAttributeError < RuntimeError
3
+ def initialize(object, attribute)
4
+ @object = object
5
+ @attribute = attribute.to_sym
6
+ end
7
+
8
+ def to_s
9
+ "Attribute #{@attribute} of #{@object.inspect} is nil."
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module Trajectory
2
+ class VelocityEqualToZeroError < RuntimeError
3
+ def initialize(project)
4
+ @project = project
5
+ end
6
+
7
+ def to_s
8
+ "Estimated velocity of #{@project.inspect} is equal to 0."
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ require 'trajectory/exceptions/missing_attribute_error'
2
+ require 'trajectory/exceptions/velocity_equal_to_zero_error'
3
+ require 'trajectory/exceptions/bad_environment_error'
@@ -0,0 +1,3 @@
1
+ module Trajectory
2
+ VERSION = "0.1.0"
3
+ end
data/lib/trajectory.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'virtus'
2
+ require 'httparty'
3
+ require 'json'
4
+
5
+ require "trajectory/version"
6
+
7
+ require 'trajectory/core_ext'
8
+ require 'trajectory/exceptions'
9
+ require 'trajectory/domain'
10
+ require 'trajectory/data_access'
11
+
12
+ require 'trajectory/client'
13
+
14
+ module Trajectory
15
+ end
@@ -0,0 +1,18 @@
1
+ Fabricator(:iteration, class_name: Trajectory::Iteration) do
2
+ id { sequence }
3
+ percent_complete 90.0
4
+ started_stories_count 4
5
+ created_at DateTime.now
6
+ estimated_velocity 12
7
+ delivered_stories_count 28
8
+ updated_at DateTime.now
9
+ unstarted_stories_count 31
10
+ starts_on DateTime.now
11
+ current false
12
+ stories_count 59
13
+ complete false
14
+ accepted_points 51
15
+ estimated_points 72
16
+ comments_count 8
17
+ accepted_stories_count 24
18
+ end
@@ -0,0 +1,11 @@
1
+ Fabricator(:project, class_name: Trajectory::Project) do
2
+ archived false
3
+ created_at DateTime.now
4
+ estimated_velocity 20
5
+ historic_velocity [10, 9, 12, 13, 18]
6
+ id { sequence(:id, 1) }
7
+ keyword 'keyword'
8
+ updated_at DateTime.now
9
+ completed_iterations_count 10
10
+ completed_stories_count 42
11
+ end
@@ -0,0 +1,18 @@
1
+ Fabricator(:story, class_name: Trajectory::Story) do
2
+ assignee_name 'An asignee'
3
+ task_type 'Feature'
4
+ position { sequence }
5
+ created_at DateTime.now
6
+ state_events []
7
+ title 'A story title'
8
+ design_needed true
9
+ updated_at DateTime.now
10
+ idea_subject 'the related idea subject'
11
+ archived false
12
+ points 1
13
+ id { sequence }
14
+ development_needed true
15
+ deleted false
16
+ user_name 'A user'
17
+ comments_count 24
18
+ end