Skip to content

Commit ec58c51

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 d9b6522 commit ec58c51

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
revision: string;
3435
}
@@ -535,6 +536,19 @@ class ChannelMap {
535536
el.innerHTML = tbody.join("");
536537
}
537538

539+
/**
540+
* Format file size in human readable format
541+
* @param {number} bytes - Size in bytes
542+
* @returns {string} - Formatted size string
543+
*/
544+
formatSize(bytes: number): string {
545+
if (bytes === 0) return "0 B";
546+
const k = 1000;
547+
const sizes = ["B", "kB", "MB", "GB", "TB"];
548+
const i = Math.floor(Math.log(bytes) / Math.log(k));
549+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
550+
}
551+
538552
/**
539553
* Prepare the channel map tables
540554
*
@@ -601,6 +615,7 @@ class ChannelMap {
601615
trackName,
602616
trackInfo["risk"],
603617
trackInfo["version"],
618+
this.formatSize(trackInfo["size"]),
604619
trackInfo["released-at"],
605620
trackInfo["confinement"],
606621
]);

templates/store/snap-details/_channel_map.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@
116116
<tr>
117117
<th>Channel</th>
118118
<th>Version</th>
119-
<th width="25%" class="u-hide--medium u-hide--small">Published</th>
119+
<th width="18%" class="u-hide--medium u-hide--small">Download Size</th>
120+
<th width="20%" class="u-hide--medium u-hide--small">Published</th>
120121
<th width="12%"></th>
121122
</tr>
122123
</thead>
@@ -169,10 +170,11 @@
169170
</div>
170171
</script>
171172
<script type="text/template" id="channel-map-row-template" data-js="channel-map-row">
172-
<tr class="${rowClass}" data-js="slide-install-instructions" data-channel="${row[0]}/${row[1]}" data-confinement="${row[4]}">
173+
<tr class="${rowClass}" data-js="slide-install-instructions" data-channel="${row[0]}/${row[1]}" data-confinement="${row[5]}">
173174
<td>${row[0]}/${row[1]}</td>
174175
<td>${row[2]}</td>
175176
<td class="u-hide--medium u-hide--small">${row[3]}</td>
177+
<td class="u-hide--medium u-hide--small">${row[4]}</td>
176178
<td class="u-align--center"><a href="#" class="p-channel-map__version-table-install">Install &rsaquo;</a></td>
177179
</tr>
178180
</script>

tests/store/tests_details.py

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

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

0 commit comments

Comments
 (0)