Skip to content

Commit 75449e7

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 b67ebcb commit 75449e7

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
@@ -369,6 +369,77 @@ def test_user_not_connected(self):
369369
assert response.status_code == 200
370370
self.assert_context("is_users_snap", False)
371371

372+
@responses.activate
373+
def test_package_size_in_channel_map(self):
374+
"""Test that package size is correctly included in channel map data"""
375+
payload = {
376+
"snap-id": "id",
377+
"name": "test-snap",
378+
"default-track": None,
379+
"snap": {
380+
"title": "Test Snap",
381+
"summary": "This is a test snap",
382+
"description": "this is a description",
383+
"media": [],
384+
"license": "MIT",
385+
"publisher": {
386+
"display-name": "Test Publisher",
387+
"username": "testpub",
388+
"validation": True,
389+
},
390+
"categories": [{"name": "test"}],
391+
"trending": False,
392+
"unlisted": False,
393+
"links": {},
394+
},
395+
"channel-map": [
396+
{
397+
"channel": {
398+
"architecture": "amd64",
399+
"name": "stable",
400+
"risk": "stable",
401+
"track": "latest",
402+
"released-at": "2018-09-18T14:45:28.064633+00:00",
403+
},
404+
"created-at": "2018-09-18T14:45:28.064633+00:00",
405+
"version": "1.0",
406+
"confinement": "strict",
407+
"download": {"size": 52428800}, # 50 MB
408+
}
409+
],
410+
}
411+
412+
responses.add(
413+
responses.Response(
414+
method="GET", url=self.api_url, json=payload, status=200
415+
)
416+
)
417+
418+
metrics_url = "https://api.snapcraft.io/api/v1/snaps/metrics"
419+
responses.add(
420+
responses.Response(
421+
method="POST", url=metrics_url, json={}, status=200
422+
)
423+
)
424+
425+
response = self.client.get(self.endpoint_url)
426+
427+
assert response.status_code == 200
428+
# Check that channel_map contains size information
429+
context = self.get_context_variable("channel_map")
430+
self.assertIsNotNone(context)
431+
# Check that size is included in the channel map data structure
432+
self.assertIn("amd64", context)
433+
self.assertIn("latest", context["amd64"])
434+
channel_data = context["amd64"]["latest"][0]
435+
self.assertEqual(channel_data["size"], 52428800)
436+
# Check that the Download Size column header is in the HTML
437+
expected_header = (
438+
b'<th width="18%" class="u-hide--medium u-hide--small">'
439+
b"Download Size</th>"
440+
)
441+
self.assertIn(expected_header, response.data)
442+
372443
@responses.activate
373444
def test_user_connected_on_not_own_snap(self):
374445
payload = {

0 commit comments

Comments
 (0)