Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit cbf24c4

Browse files
authored
feat: Add bundle badge svgs (#1235)
1 parent a501266 commit cbf24c4

File tree

2 files changed

+300
-2
lines changed

2 files changed

+300
-2
lines changed

graphs/helpers/badge.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def get_badge(coverage: str | None, coverage_range: list[int], precision: str):
3636
)
3737

3838

39-
def format_coverage_precision(coverage, precision):
39+
def format_coverage_precision(coverage: float | None, precision: int):
4040
"""
4141
Returns coverage as a string formatted with appropriate precision
4242
@@ -50,3 +50,53 @@ def format_coverage_precision(coverage, precision):
5050
precision = int(precision)
5151
coverage = float(coverage)
5252
return ("%%.%sf" % precision) % coverage
53+
54+
55+
def get_bundle_badge(bundle_size_bytes: int, precision: int):
56+
bundle_size_string = format_bundle_bytes(bundle_size_bytes, precision)
57+
char_width = 7 # approximate, looks good on all reasonable inputs
58+
width_in_pixels = len(bundle_size_string) * char_width
59+
static_width = 57 # width of static elements in the svg (text + margins)
60+
61+
width = static_width + width_in_pixels
62+
63+
return f""" <svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="20">
64+
<linearGradient id="CodecovBadgeGradient" x2="0" y2="100%">
65+
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
66+
<stop offset="1" stop-opacity=".1" />
67+
</linearGradient>
68+
<mask id="CodecovBadgeMask{width}px">
69+
<rect width="{width}" height="20" rx="3" fill="#fff" />
70+
</mask>
71+
<g mask="url(#CodecovBadgeMask{width}px)">
72+
<path fill="#555" d="M0 0h47v20H0z" />
73+
<path fill="#2C2433" d="M47 0h{width - static_width + 10}v20H47z" />
74+
<path fill="url(#CodecovBadgeGradient)" d="M0 0h{width}v20H0z" />
75+
</g>
76+
<g fill="#fff" text-anchor="left" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
77+
<text x="5" y="15" fill="#010101" fill-opacity=".3">bundle</text>
78+
<text x="5" y="14">bundle</text>
79+
<text x="52" y="15" fill="#010101" fill-opacity=".3">{bundle_size_string}</text>
80+
<text x="52" y="14">{bundle_size_string}</text>
81+
</g>
82+
</svg>
83+
"""
84+
85+
86+
def format_bundle_bytes(bytes: int, precision: int):
87+
precision = min(abs(precision), 2) # allow at most 2 decimal places
88+
kilobyte = 10**3
89+
megabyte = 10**6
90+
gigabyte = 10**9
91+
92+
def remove_trailing_zeros(n: str):
93+
return (n.rstrip("0") if "." in n else n).rstrip(".")
94+
95+
if bytes < kilobyte:
96+
return f"{bytes}B"
97+
elif bytes < megabyte:
98+
return f"{remove_trailing_zeros(str(round(bytes / kilobyte, precision)))}KB"
99+
elif bytes < gigabyte:
100+
return f"{remove_trailing_zeros(str(round(bytes / megabyte, precision)))}MB"
101+
else:
102+
return f"{remove_trailing_zeros(str(round(bytes / gigabyte, precision)))}GB"

graphs/tests/test_helpers.py

Lines changed: 249 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
from graphs.helpers.badge import format_coverage_precision, get_badge
1+
from graphs.helpers.badge import (
2+
format_bundle_bytes,
3+
format_coverage_precision,
4+
get_badge,
5+
get_bundle_badge,
6+
)
27

38

49
class TestGraphsHelpers(object):
@@ -157,3 +162,246 @@ def test_unknown_badge(self):
157162
_badge = [line.strip() for line in _badge.split("\n")]
158163
expected_badge = [line.strip() for line in expected_badge.split("\n")]
159164
assert expected_badge == _badge
165+
166+
def test_bundle_badge_small(self):
167+
bundle_size_bytes = 7
168+
precision = 2
169+
170+
expected_badge = """<svg xmlns="http://www.w3.org/2000/svg" width="71" height="20">
171+
<linearGradient id="CodecovBadgeGradient" x2="0" y2="100%">
172+
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
173+
<stop offset="1" stop-opacity=".1" />
174+
</linearGradient>
175+
<mask id="CodecovBadgeMask71px">
176+
<rect width="71" height="20" rx="3" fill="#fff" />
177+
</mask>
178+
<g mask="url(#CodecovBadgeMask71px)">
179+
<path fill="#555" d="M0 0h47v20H0z" />
180+
<path fill="#2C2433" d="M47 0h24v20H47z" />
181+
<path fill="url(#CodecovBadgeGradient)" d="M0 0h71v20H0z" />
182+
</g>
183+
<g fill="#fff" text-anchor="left" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
184+
<text x="5" y="15" fill="#010101" fill-opacity=".3">bundle</text>
185+
<text x="5" y="14">bundle</text>
186+
<text x="52" y="15" fill="#010101" fill-opacity=".3">7B</text>
187+
<text x="52" y="14">7B</text>
188+
</g>
189+
</svg>
190+
"""
191+
192+
_badge = get_bundle_badge(bundle_size_bytes, precision)
193+
_badge = [line.strip() for line in _badge.split("\n")]
194+
expected_badge = [line.strip() for line in expected_badge.split("\n")]
195+
assert expected_badge == _badge
196+
197+
def test_bundle_badge_medium(self):
198+
bundle_size_bytes = 7777777
199+
precision = 2
200+
201+
expected_badge = """<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20">
202+
<linearGradient id="CodecovBadgeGradient" x2="0" y2="100%">
203+
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
204+
<stop offset="1" stop-opacity=".1" />
205+
</linearGradient>
206+
<mask id="CodecovBadgeMask99px">
207+
<rect width="99" height="20" rx="3" fill="#fff" />
208+
</mask>
209+
<g mask="url(#CodecovBadgeMask99px)">
210+
<path fill="#555" d="M0 0h47v20H0z" />
211+
<path fill="#2C2433" d="M47 0h52v20H47z" />
212+
<path fill="url(#CodecovBadgeGradient)" d="M0 0h99v20H0z" />
213+
</g>
214+
<g fill="#fff" text-anchor="left" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
215+
<text x="5" y="15" fill="#010101" fill-opacity=".3">bundle</text>
216+
<text x="5" y="14">bundle</text>
217+
<text x="52" y="15" fill="#010101" fill-opacity=".3">7.78MB</text>
218+
<text x="52" y="14">7.78MB</text>
219+
</g>
220+
</svg>
221+
"""
222+
223+
_badge = get_bundle_badge(bundle_size_bytes, precision)
224+
_badge = [line.strip() for line in _badge.split("\n")]
225+
expected_badge = [line.strip() for line in expected_badge.split("\n")]
226+
assert expected_badge == _badge
227+
228+
def test_bundle_badge_large(self):
229+
bundle_size_bytes = 7777777777777
230+
precision = 2
231+
232+
expected_badge = """<svg xmlns="http://www.w3.org/2000/svg" width="120" height="20">
233+
<linearGradient id="CodecovBadgeGradient" x2="0" y2="100%">
234+
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
235+
<stop offset="1" stop-opacity=".1" />
236+
</linearGradient>
237+
<mask id="CodecovBadgeMask120px">
238+
<rect width="120" height="20" rx="3" fill="#fff" />
239+
</mask>
240+
<g mask="url(#CodecovBadgeMask120px)">
241+
<path fill="#555" d="M0 0h47v20H0z" />
242+
<path fill="#2C2433" d="M47 0h73v20H47z" />
243+
<path fill="url(#CodecovBadgeGradient)" d="M0 0h120v20H0z" />
244+
</g>
245+
<g fill="#fff" text-anchor="left" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
246+
<text x="5" y="15" fill="#010101" fill-opacity=".3">bundle</text>
247+
<text x="5" y="14">bundle</text>
248+
<text x="52" y="15" fill="#010101" fill-opacity=".3">7777.78GB</text>
249+
<text x="52" y="14">7777.78GB</text>
250+
</g>
251+
</svg>
252+
"""
253+
254+
_badge = get_bundle_badge(bundle_size_bytes, precision)
255+
_badge = [line.strip() for line in _badge.split("\n")]
256+
expected_badge = [line.strip() for line in expected_badge.split("\n")]
257+
assert expected_badge == _badge
258+
259+
def test_format_bundle_bytes_0_precision(self):
260+
bundle_sizes = [
261+
7,
262+
77,
263+
777,
264+
7777,
265+
77777,
266+
777777,
267+
7777777,
268+
77777777,
269+
777777777,
270+
7777777777,
271+
77777777777,
272+
777777777777,
273+
7777777777777,
274+
]
275+
276+
expected = [
277+
"7B",
278+
"77B",
279+
"777B",
280+
"8KB",
281+
"78KB",
282+
"778KB",
283+
"8MB",
284+
"78MB",
285+
"778MB",
286+
"8GB",
287+
"78GB",
288+
"778GB",
289+
"7778GB",
290+
]
291+
292+
for i in range(len(bundle_sizes)):
293+
assert format_bundle_bytes(bundle_sizes[i], 0) == expected[i]
294+
295+
def test_format_bundle_bytes_1_precision(self):
296+
bundle_sizes = [
297+
7,
298+
77,
299+
777,
300+
7777,
301+
77777,
302+
777777,
303+
7777777,
304+
77777777,
305+
777777777,
306+
7777777777,
307+
77777777777,
308+
777777777777,
309+
7777777777777,
310+
]
311+
312+
expected = [
313+
"7B",
314+
"77B",
315+
"777B",
316+
"7.8KB",
317+
"77.8KB",
318+
"777.8KB",
319+
"7.8MB",
320+
"77.8MB",
321+
"777.8MB",
322+
"7.8GB",
323+
"77.8GB",
324+
"777.8GB",
325+
"7777.8GB",
326+
]
327+
328+
for i in range(len(bundle_sizes)):
329+
assert format_bundle_bytes(bundle_sizes[i], 1) == expected[i]
330+
331+
def test_format_bundle_bytes_2_precision(self):
332+
bundle_sizes = [
333+
7,
334+
77,
335+
777,
336+
7777,
337+
77777,
338+
777777,
339+
7777777,
340+
77777777,
341+
777777777,
342+
7777777777,
343+
77777777777,
344+
777777777777,
345+
7777777777777,
346+
]
347+
348+
expected = [
349+
"7B",
350+
"77B",
351+
"777B",
352+
"7.78KB",
353+
"77.78KB",
354+
"777.78KB",
355+
"7.78MB",
356+
"77.78MB",
357+
"777.78MB",
358+
"7.78GB",
359+
"77.78GB",
360+
"777.78GB",
361+
"7777.78GB",
362+
]
363+
364+
for i in range(len(bundle_sizes)):
365+
assert format_bundle_bytes(bundle_sizes[i], 2) == expected[i]
366+
367+
def test_format_bundle_strips_zeros(self):
368+
bundle_sizes = [
369+
0,
370+
10,
371+
100,
372+
1000,
373+
10000,
374+
100000,
375+
1000000,
376+
10000000,
377+
100000000,
378+
1000000000,
379+
10000000000,
380+
100000000000,
381+
1000000000000,
382+
1100,
383+
11100,
384+
111100,
385+
]
386+
387+
expected = [
388+
"0B",
389+
"10B",
390+
"100B",
391+
"1KB",
392+
"10KB",
393+
"100KB",
394+
"1MB",
395+
"10MB",
396+
"100MB",
397+
"1GB",
398+
"10GB",
399+
"100GB",
400+
"1000GB",
401+
"1.1KB",
402+
"11.1KB",
403+
"111.1KB",
404+
]
405+
406+
for i in range(len(bundle_sizes)):
407+
assert format_bundle_bytes(bundle_sizes[i], 2) == expected[i]

0 commit comments

Comments
 (0)