Skip to content

Commit 0121168

Browse files
committed
feat: Add download size display to channel table
- Add "Download Size" column to channel map table showing per-channel package sizes - Implement human-readable size formatting (B, kB, MB, GB, TB) in JavaScript - Update ChannelData TypeScript interface to include size field - Add responsive design (hidden on mobile screens) - Include comprehensive unit tests for size display functionality Addresses user need for bandwidth awareness on metered connections by showing accurate download sizes for each channel, since different channels can have different package sizes. Fixes #1864
1 parent fedf8ec commit 0121168

3 files changed

Lines changed: 90 additions & 2 deletions

File tree

static/js/public/snap-details/channelMap.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ interface ChannelData {
2929
"released-at": string;
3030
risk: string;
3131
version: string;
32+
size: number;
3233
channel?: string;
3334
}
3435

@@ -517,6 +518,19 @@ class ChannelMap {
517518
el.innerHTML = tbody.join("");
518519
}
519520

521+
/**
522+
* Format file size in human readable format
523+
* @param {number} bytes - Size in bytes
524+
* @returns {string} - Formatted size string
525+
*/
526+
formatSize(bytes: number): string {
527+
if (bytes === 0) return "0 B";
528+
const k = 1000;
529+
const sizes = ["B", "kB", "MB", "GB", "TB"];
530+
const i = Math.floor(Math.log(bytes) / Math.log(k));
531+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
532+
}
533+
520534
/**
521535
* Prepare the channel map tables
522536
*
@@ -574,6 +588,7 @@ class ChannelMap {
574588
trackName,
575589
trackInfo["risk"],
576590
trackInfo["version"],
591+
this.formatSize(trackInfo["size"]),
577592
trackInfo["released-at"],
578593
trackInfo["confinement"],
579594
]);

templates/store/snap-details/_channel_map.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@
9797
<tr>
9898
<th>Channel</th>
9999
<th>Version</th>
100-
<th width="25%" class="u-hide--medium u-hide--small">Published</th>
100+
<th width="18%" class="u-hide--medium u-hide--small">Download Size</th>
101+
<th width="20%" class="u-hide--medium u-hide--small">Published</th>
101102
<th width="12%"></th>
102103
</tr>
103104
</thead>
@@ -136,10 +137,11 @@
136137
</div>
137138
</script>
138139
<script type="text/template" id="channel-map-row-template" data-js="channel-map-row">
139-
<tr class="${rowClass}" data-js="slide-install-instructions" data-channel="${row[0]}/${row[1]}" data-confinement="${row[4]}">
140+
<tr class="${rowClass}" data-js="slide-install-instructions" data-channel="${row[0]}/${row[1]}" data-confinement="${row[5]}">
140141
<td>${row[0]}/${row[1]}</td>
141142
<td>${row[2]}</td>
142143
<td class="u-hide--medium u-hide--small">${row[3]}</td>
144+
<td class="u-hide--medium u-hide--small">${row[4]}</td>
143145
<td class="u-align--center"><a href="#" class="p-channel-map__version-table-install">Install &rsaquo;</a></td>
144146
</tr>
145147
</script>

tests/store/tests_details.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,77 @@ def test_user_not_connected(self):
252252
assert response.status_code == 200
253253
self.assert_context("is_users_snap", False)
254254

255+
@responses.activate
256+
def test_package_size_in_channel_map(self):
257+
"""Test that package size is correctly included in channel map data"""
258+
payload = {
259+
"snap-id": "id",
260+
"name": "test-snap",
261+
"default-track": None,
262+
"snap": {
263+
"title": "Test Snap",
264+
"summary": "This is a test snap",
265+
"description": "this is a description",
266+
"media": [],
267+
"license": "MIT",
268+
"publisher": {
269+
"display-name": "Test Publisher",
270+
"username": "testpub",
271+
"validation": True,
272+
},
273+
"categories": [{"name": "test"}],
274+
"trending": False,
275+
"unlisted": False,
276+
"links": {},
277+
},
278+
"channel-map": [
279+
{
280+
"channel": {
281+
"architecture": "amd64",
282+
"name": "stable",
283+
"risk": "stable",
284+
"track": "latest",
285+
"released-at": "2018-09-18T14:45:28.064633+00:00",
286+
},
287+
"created-at": "2018-09-18T14:45:28.064633+00:00",
288+
"version": "1.0",
289+
"confinement": "strict",
290+
"download": {"size": 52428800}, # 50 MB
291+
}
292+
],
293+
}
294+
295+
responses.add(
296+
responses.Response(
297+
method="GET", url=self.api_url, json=payload, status=200
298+
)
299+
)
300+
301+
metrics_url = "https://api.snapcraft.io/api/v1/snaps/metrics"
302+
responses.add(
303+
responses.Response(
304+
method="POST", url=metrics_url, json={}, status=200
305+
)
306+
)
307+
308+
response = self.client.get(self.endpoint_url)
309+
310+
assert response.status_code == 200
311+
# Check that channel_map contains size information
312+
context = self.get_context_variable("channel_map")
313+
self.assertIsNotNone(context)
314+
# Check that size is included in the channel map data structure
315+
self.assertIn("amd64", context)
316+
self.assertIn("latest", context["amd64"])
317+
channel_data = context["amd64"]["latest"][0]
318+
self.assertEqual(channel_data["size"], 52428800)
319+
# Check that the Download Size column header is in the HTML
320+
expected_header = (
321+
b'<th width="18%" class="u-hide--medium u-hide--small">'
322+
b"Download Size</th>"
323+
)
324+
self.assertIn(expected_header, response.data)
325+
255326
@responses.activate
256327
def test_user_connected_on_not_own_snap(self):
257328
payload = {

0 commit comments

Comments
 (0)