Class: ExecutableStories::Story

Inherits:
Object
  • Object
show all
Defined in:
lib/executable_stories/story.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(scenario, tags: nil, ticket: nil, meta: nil, trace_url_template: nil) ⇒ Story

Returns a new instance of Story.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/executable_stories/story.rb', line 16

def initialize(scenario, tags: nil, ticket: nil, meta: nil, trace_url_template: nil)
  @scenario = scenario
  @steps = []
  @tags = tags
  @tickets = normalize_tickets(ticket)
  @meta = meta&.dup
  @docs = []
  @current_step = nil
  @seen_primary = {}
  @start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000.0
  @source_order = Collector.next_order
  @step_counter = 0
  @attachments = []
  @active_timers = {}
  @timer_counter = 0
  @otel_spans = nil
  @trace_url_template = trace_url_template
  @suite_path = nil

  bridge_otel
end

Instance Attribute Details

#active_timers ⇒ Object

Returns the value of attribute active_timers.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def active_timers
  @active_timers
end

#attachments ⇒ Object

Returns the value of attribute attachments.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def attachments
  @attachments
end

#current_step ⇒ Object

Returns the value of attribute current_step.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def current_step
  @current_step
end

#docs ⇒ Object

Returns the value of attribute docs.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def docs
  @docs
end

#end_time ⇒ Object

Returns the value of attribute end_time.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def end_time
  @end_time
end

#meta ⇒ Object

Returns the value of attribute meta.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def meta
  @meta
end

#otel_spans ⇒ Object

Returns the value of attribute otel_spans.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def otel_spans
  @otel_spans
end

#scenario ⇒ Object

Returns the value of attribute scenario.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def scenario
  @scenario
end

#seen_primary ⇒ Object

Returns the value of attribute seen_primary.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def seen_primary
  @seen_primary
end

#source_order ⇒ Object

Returns the value of attribute source_order.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def source_order
  @source_order
end

#start_time ⇒ Object

Returns the value of attribute start_time.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def start_time
  @start_time
end

#step_counter ⇒ Object

Returns the value of attribute step_counter.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def step_counter
  @step_counter
end

#steps ⇒ Object

Returns the value of attribute steps.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def steps
  @steps
end

#suite_path ⇒ Object

Returns the value of attribute suite_path.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def suite_path
  @suite_path
end

#tags ⇒ Object

Returns the value of attribute tags.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def tags
  @tags
end

#tickets ⇒ Object

Returns the value of attribute tickets.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def tickets
  @tickets
end

#timer_counter ⇒ Object

Returns the value of attribute timer_counter.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def timer_counter
  @timer_counter
end

#trace_url_template ⇒ Object

Returns the value of attribute trace_url_template.



11
12
13
# File 'lib/executable_stories/story.rb', line 11

def trace_url_template
  @trace_url_template
end

Instance Method Details

#act(text) ⇒ Object



68
69
70
71
# File 'lib/executable_stories/story.rb', line 68

def act(text)
  add_step("When", text)
  self
end

#action(text) ⇒ Object



93
94
95
96
# File 'lib/executable_stories/story.rb', line 93

def action(text)
  add_step("When", text)
  self
end

#and(text) ⇒ Object



53
54
55
56
# File 'lib/executable_stories/story.rb', line 53

def and(text)
  add_explicit_step("And", text)
  self
end

#arrange(text) ⇒ Object



63
64
65
66
# File 'lib/executable_stories/story.rb', line 63

def arrange(text)
  add_step("Given", text)
  self
end

#assert_that(text) ⇒ Object



73
74
75
76
# File 'lib/executable_stories/story.rb', line 73

def assert_that(text)
  add_step("Then", text)
  self
end

#attach(name, media_type, path: nil, body: nil, encoding: nil, charset: nil, file_name: nil) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/executable_stories/story.rb', line 217

def attach(name, media_type, path: nil, body: nil, encoding: nil, charset: nil, file_name: nil)
  att = RawAttachment.new(
    name: name,
    media_type: media_type,
    path: path,
    body: body,
    encoding: encoding,
    charset: charset,
    file_name: file_name
  )
  if @current_step
    idx = @steps.index(@current_step)
    att.step_index = idx if idx
    att.step_id = @current_step.id
  end
  @attachments << att
  self
end

#attach_inline(name, media_type, body, encoding: "IDENTITY") ⇒ Object



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/executable_stories/story.rb', line 236

def attach_inline(name, media_type, body, encoding: "IDENTITY")
  att = RawAttachment.new(
    name: name,
    media_type: media_type,
    body: body,
    encoding: encoding
  )
  if @current_step
    idx = @steps.index(@current_step)
    att.step_index = idx if idx
    att.step_id = @current_step.id
  end
  @attachments << att
  self
end

#attach_spans(spans) ⇒ Object



252
253
254
255
# File 'lib/executable_stories/story.rb', line 252

def attach_spans(spans)
  @otel_spans = spans
  self
end

#but(text) ⇒ Object



58
59
60
61
# File 'lib/executable_stories/story.rb', line 58

def but(text)
  add_explicit_step("But", text)
  self
end

#code(label, content, lang: nil, children: nil) ⇒ Object



175
176
177
178
179
# File 'lib/executable_stories/story.rb', line 175

def code(label, content, lang: nil, children: nil)
  entry = DocEntry.code(label, content, lang: lang, children: children)
  attach_doc(entry)
  entry
end

#context(text) ⇒ Object



83
84
85
86
# File 'lib/executable_stories/story.rb', line 83

def context(text)
  add_step("Given", text)
  self
end

#custom(type, data, children: nil) ⇒ Object



211
212
213
214
215
# File 'lib/executable_stories/story.rb', line 211

def custom(type, data, children: nil)
  entry = DocEntry.custom(type, data, children: children)
  attach_doc(entry)
  entry
end

#end_timer(token) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/executable_stories/story.rb', line 137

def end_timer(token)
  entry = @active_timers[token]
  return unless entry && !entry[:consumed]

  entry[:consumed] = true
  duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000.0) - entry[:start]

  step = nil
  step = @steps.find { |s| s.id == entry[:step_id] } if entry[:step_id]
  step = @steps[entry[:step_index]] if !step && entry[:step_index] && entry[:step_index] < @steps.length

  step.duration_ms = duration_ms if step
end

#execute(text) ⇒ Object



88
89
90
91
# File 'lib/executable_stories/story.rb', line 88

def execute(text)
  add_step("When", text)
  self
end

#expect(text) ⇒ Object



118
119
120
# File 'lib/executable_stories/story.rb', line 118

def expect(text, &)
  fn("Then", text, &)
end

#fn(keyword, text, &body) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/executable_stories/story.rb', line 103

def fn(keyword, text, &body)
  add_step(keyword, text)
  @current_step.wrapped = true

  start_ms = Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000.0
  begin
    result = body.call
    @current_step.duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000.0) - start_ms
    result
  rescue StandardError => e
    @current_step.duration_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000.0) - start_ms
    raise e
  end
end

#get_meta ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/executable_stories/story.rb', line 257

def get_meta
  StoryMeta.new(
    scenario: @scenario,
    steps: @steps,
    tags: @tags,
    tickets: @tickets,
    meta: @meta,
    suite_path: @suite_path,
    docs: @docs.empty? ? nil : @docs,
    source_order: @source_order,
    otel_spans: @otel_spans
  )
end

#given(text) ⇒ Object



38
39
40
41
# File 'lib/executable_stories/story.rb', line 38

def given(text)
  add_step("Given", text)
  self
end

#json(label, value, children: nil) ⇒ Object



169
170
171
172
173
# File 'lib/executable_stories/story.rb', line 169

def json(label, value, children: nil)
  entry = DocEntry.json_doc(label, value, children: children)
  attach_doc(entry)
  entry
end

#kv(label, value, children: nil) ⇒ Object



163
164
165
166
167
# File 'lib/executable_stories/story.rb', line 163

def kv(label, value, children: nil)
  entry = DocEntry.kv(label, value, children: children)
  attach_doc(entry)
  entry
end


187
188
189
190
191
# File 'lib/executable_stories/story.rb', line 187

def link(label, url, children: nil)
  entry = DocEntry.link(label, url, children: children)
  attach_doc(entry)
  entry
end

#mermaid(code, title: nil, children: nil) ⇒ Object



199
200
201
202
203
# File 'lib/executable_stories/story.rb', line 199

def mermaid(code, title: nil, children: nil)
  entry = DocEntry.mermaid(code, title: title, children: children)
  attach_doc(entry)
  entry
end

#note(text, children: nil) ⇒ Object



151
152
153
154
155
# File 'lib/executable_stories/story.rb', line 151

def note(text, children: nil)
  entry = DocEntry.note(text, children: children)
  attach_doc(entry)
  entry
end

#record(status:, title: nil, suite_path: nil, source_file: nil, source_line: nil, duration_ms: nil, error: nil) ⇒ Object



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/executable_stories/story.rb', line 271

def record(status:, title: nil, suite_path: nil, source_file: nil, source_line: nil, duration_ms: nil, error: nil)
  @end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000.0
  @suite_path = suite_path if suite_path
  duration = duration_ms || (@end_time - @start_time)

  step_events = @steps.each_with_index.map do |s, i|
    s.duration_ms ? RawStepEvent.new(index: i, title: s.text, duration_ms: s.duration_ms) : nil
  end.compact

  tc = RawTestCase.new(
    status: status,
    title: title || @scenario,
    title_path: suite_path ? suite_path + [@scenario] : [@scenario],
    story: get_meta,
    source_file: source_file,
    source_line: source_line,
    duration_ms: duration,
    error: error,
    retry: 0,
    retries: 0,
    attachments: @attachments.empty? ? nil : @attachments,
    step_events: step_events.empty? ? nil : step_events,
    start_time: @start_time,
    end_time: @end_time
  )
  Collector.record(tc)
  tc
end

#screenshot(path, alt: nil, children: nil) ⇒ Object



205
206
207
208
209
# File 'lib/executable_stories/story.rb', line 205

def screenshot(path, alt: nil, children: nil)
  entry = DocEntry.screenshot(path, alt: alt, children: children)
  attach_doc(entry)
  entry
end

#section(title, markdown, children: nil) ⇒ Object



193
194
195
196
197
# File 'lib/executable_stories/story.rb', line 193

def section(title, markdown, children: nil)
  entry = DocEntry.section(title, markdown, children: children)
  attach_doc(entry)
  entry
end

#setup(text) ⇒ Object



78
79
80
81
# File 'lib/executable_stories/story.rb', line 78

def setup(text)
  add_step("Given", text)
  self
end

#start_timer ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/executable_stories/story.rb', line 122

def start_timer
  token = @timer_counter
  @timer_counter += 1

  step_index = @current_step ? @steps.index(@current_step) : nil
  entry = {
    start: Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000.0,
    step_index: step_index,
    step_id: @current_step&.id,
    consumed: false
  }
  @active_timers[token] = entry
  token
end

#table(label, columns, rows, children: nil) ⇒ Object



181
182
183
184
185
# File 'lib/executable_stories/story.rb', line 181

def table(label, columns, rows, children: nil)
  entry = DocEntry.table(label, columns, rows, children: children)
  attach_doc(entry)
  entry
end

#tag(*names, children: nil) ⇒ Object



157
158
159
160
161
# File 'lib/executable_stories/story.rb', line 157

def tag(*names, children: nil)
  entry = DocEntry.tag(*names, children: children)
  attach_doc(entry)
  entry
end

#then(text) ⇒ Object



48
49
50
51
# File 'lib/executable_stories/story.rb', line 48

def then(text)
  add_step("Then", text)
  self
end

#verify(text) ⇒ Object



98
99
100
101
# File 'lib/executable_stories/story.rb', line 98

def verify(text)
  add_step("Then", text)
  self
end

#when(text) ⇒ Object



43
44
45
46
# File 'lib/executable_stories/story.rb', line 43

def when(text)
  add_step("When", text)
  self
end