Skip to content

Commit 8356125

Browse files
committed
Add Deflate and ZLib compression formats
Extends the format system to support all four algorithms available in System.IO.Compression with no additional dependencies. The architecture is data-driven throughout so adding a format is a one-liner in each switch expression / label object. - App.razor: add Deflate and ZLib to CompressionFormat enum; replace binary ternaries with switch expressions for FormatName, Extension, OnInitialized, CreateCompressStream, and CreateDecompressStream - wwwroot/index.html: replace brotli-specific script with a formats array + labels object that covers brotli, deflate, and zlib - e2e/tests/app.spec.js: replace three separate describe blocks with a for-loop over ['brotli', 'deflate', 'zlib'] generating title/heading and paragraph tests for each format (13 tests, all passing)
1 parent dd5863f commit 8356125

3 files changed

Lines changed: 94 additions & 44 deletions

File tree

App.razor

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@
5353
<div class="mb-3">
5454
@if (compressionMode == CompressionMode.Compress)
5555
{
56-
<label class="form-label">Choose files to compress to @(_format == CompressionFormat.Brotli ? "brotli" : "gzip")</label>
56+
<label class="form-label">Choose files to compress to @FormatName</label>
5757
}
5858
else
5959
{
60-
<label class="form-label">Choose @(_format == CompressionFormat.Brotli ? "brotli" : "gzip") files to decompress</label>
60+
<label class="form-label">Choose @FormatName files to decompress</label>
6161
}
6262
<InputFile OnChange="OnFilesChange" multiple class="form-control" />
6363
</div>
@@ -103,31 +103,56 @@
103103
</ul>
104104

105105
@code {
106-
private enum CompressionFormat { GZip, Brotli }
106+
private enum CompressionFormat { GZip, Brotli, Deflate, ZLib }
107107

108108
private CompressionMode compressionMode = CompressionMode.Compress;
109109
private CompressionLevel compressionLevel = CompressionLevel.Optimal;
110110
private List<Models.File> files = new List<Models.File>();
111111
private bool anyFiles => files.Any();
112112

113113
private CompressionFormat _format = CompressionFormat.GZip;
114-
private string Extension => _format == CompressionFormat.Brotli ? ".br" : ".gz";
114+
private string FormatName => _format switch
115+
{
116+
CompressionFormat.Brotli => "brotli",
117+
CompressionFormat.Deflate => "deflate",
118+
CompressionFormat.ZLib => "zlib",
119+
_ => "gzip"
120+
};
121+
private string Extension => _format switch
122+
{
123+
CompressionFormat.Brotli => ".br",
124+
CompressionFormat.Deflate => ".deflate",
125+
CompressionFormat.ZLib => ".zlib",
126+
_ => ".gz"
127+
};
115128

116129
protected override void OnInitialized()
117130
{
118-
if (new Uri(NavManager.Uri).Host.Contains("brotli"))
119-
_format = CompressionFormat.Brotli;
131+
var host = new Uri(NavManager.Uri).Host;
132+
_format = host switch
133+
{
134+
_ when host.Contains("brotli") => CompressionFormat.Brotli,
135+
_ when host.Contains("deflate") => CompressionFormat.Deflate,
136+
_ when host.Contains("zlib") => CompressionFormat.ZLib,
137+
_ => CompressionFormat.GZip
138+
};
120139
}
121140

122-
private Stream CreateCompressStream(Stream output, CompressionLevel level) =>
123-
_format == CompressionFormat.Brotli
124-
? new BrotliStream(output, level)
125-
: new GZipStream(output, level);
141+
private Stream CreateCompressStream(Stream output, CompressionLevel level) => _format switch
142+
{
143+
CompressionFormat.Brotli => new BrotliStream(output, level),
144+
CompressionFormat.Deflate => new DeflateStream(output, level),
145+
CompressionFormat.ZLib => new ZLibStream(output, level),
146+
_ => new GZipStream(output, level)
147+
};
126148

127-
private Stream CreateDecompressStream(Stream input) =>
128-
_format == CompressionFormat.Brotli
129-
? new BrotliStream(input, CompressionMode.Decompress)
130-
: new GZipStream(input, CompressionMode.Decompress);
149+
private Stream CreateDecompressStream(Stream input) => _format switch
150+
{
151+
CompressionFormat.Brotli => new BrotliStream(input, CompressionMode.Decompress),
152+
CompressionFormat.Deflate => new DeflateStream(input, CompressionMode.Decompress),
153+
CompressionFormat.ZLib => new ZLibStream(input, CompressionMode.Decompress),
154+
_ => new GZipStream(input, CompressionMode.Decompress)
155+
};
131156

132157
private void OnFilesChange(InputFileChangeEventArgs e)
133158
{

e2e/tests/app.spec.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,25 @@ test('decompress a file end-to-end', async ({ page }) => {
8181
fs.unlinkSync(gzFile);
8282
});
8383

84-
// Brotli static content (heading, title, meta) is driven by the inline JS in index.html,
84+
// Static content (heading, title, meta, paragraph) is driven by the inline JS in index.html,
8585
// which reads window.compressionFormat. We spoof it here via addInitScript.
8686
// Blazor component behaviour (labels, file extension) uses NavigationManager and requires
87-
// a real "brotli.*" hostname, so those paths are covered by manual / deployment testing.
88-
test.describe('brotli mode', () => {
89-
test.beforeEach(async ({ page }) => {
90-
await page.addInitScript(() => { window.compressionFormat = 'brotli'; });
91-
await page.goto('/');
92-
await page.waitForSelector('.spinner-border', { state: 'hidden', timeout: 30000 });
87+
// a real hostname match, so those paths are covered by manual / deployment testing.
88+
for (const format of ['brotli', 'deflate', 'zlib']) {
89+
test.describe(`${format} mode`, () => {
90+
test.beforeEach(async ({ page }) => {
91+
await page.addInitScript((f) => { window.compressionFormat = f; }, format);
92+
await page.goto('/');
93+
await page.waitForSelector('.spinner-border', { state: 'hidden', timeout: 30000 });
94+
});
95+
96+
test(`page title and heading reflect ${format}`, async ({ page }) => {
97+
await expect(page).toHaveTitle(new RegExp(format, 'i'));
98+
await expect(page.getByRole('heading', { name: new RegExp(`Online ${format} de/compressor`, 'i') })).toBeVisible();
99+
});
100+
101+
test(`intro paragraph mentions ${format}`, async ({ page }) => {
102+
await expect(page.locator('#intro-paragraph')).toContainText(new RegExp(format, 'i'));
103+
});
93104
});
94-
95-
test('page title and heading reflect brotli', async ({ page }) => {
96-
await expect(page).toHaveTitle(/Brotli/i);
97-
await expect(page.getByRole('heading', { name: /Online Brotli de\/compressor/i })).toBeVisible();
98-
});
99-
100-
test('intro paragraph mentions brotli', async ({ page }) => {
101-
await expect(page.locator('#intro-paragraph')).toContainText(/Brotli/i);
102-
});
103-
});
105+
}

wwwroot/index.html

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,45 @@ <h1 class="display-4">Online GZIP de/compressor</h1>
143143
</footer>
144144
<script>
145145
(function () {
146-
const isBrotli = window.compressionFormat === 'brotli' || window.location.hostname.includes('brotli');
147-
if (!window.compressionFormat) window.compressionFormat = isBrotli ? 'brotli' : 'gzip';
148-
if (!isBrotli) return;
146+
const hostname = window.location.hostname;
147+
const formats = ['brotli', 'deflate', 'zlib'];
148+
const detected = formats.find(f => hostname.includes(f)) || 'gzip';
149+
if (!window.compressionFormat) window.compressionFormat = detected;
150+
const format = window.compressionFormat;
151+
if (format === 'gzip') return;
149152

150153
const set = (sel, attr, val) => { const el = document.querySelector(sel); if (el) el[attr] = val; };
151-
const title = 'Brotli decompress/compress files from your browser';
152-
const desc = 'Use this online Brotli tool to compress and decompress files right in the browser';
154+
const labels = {
155+
brotli: {
156+
title: 'Brotli decompress/compress files from your browser',
157+
desc: 'Use this online Brotli tool to compress and decompress files right in the browser',
158+
heading: 'Online Brotli de/compressor',
159+
para: 'This tool can compress and decompress files using the Brotli algorithm. The files are compressed/decompressed right inside of your browser without transmitting the files to a server.'
160+
},
161+
deflate: {
162+
title: 'Deflate decompress/compress files from your browser',
163+
desc: 'Use this online Deflate tool to compress and decompress files right in the browser',
164+
heading: 'Online Deflate de/compressor',
165+
para: 'This tool can compress and decompress files using the Deflate algorithm. The files are compressed/decompressed right inside of your browser without transmitting the files to a server.'
166+
},
167+
zlib: {
168+
title: 'ZLib decompress/compress files from your browser',
169+
desc: 'Use this online ZLib tool to compress and decompress files right in the browser',
170+
heading: 'Online ZLib de/compressor',
171+
para: 'This tool can compress and decompress files using the ZLib algorithm. The files are compressed/decompressed right inside of your browser without transmitting the files to a server.'
172+
}
173+
};
153174

154-
document.title = title;
155-
set('meta[name="description"]', 'content', desc);
156-
set('meta[property="og:title"]', 'content', title);
157-
set('meta[property="og:description"]', 'content', desc);
158-
set('meta[name="twitter:title"]', 'content', title);
159-
set('meta[name="twitter:description"]','content', desc);
160-
set('h1.display-4', 'textContent', 'Online Brotli de/compressor');
161-
set('#intro-paragraph','textContent', 'This tool can compress and decompress files using the Brotli algorithm. The files are compressed/decompressed right inside of your browser without transmitting the files to a server.');
175+
const l = labels[format];
176+
if (!l) return;
177+
document.title = l.title;
178+
set('meta[name="description"]', 'content', l.desc);
179+
set('meta[property="og:title"]', 'content', l.title);
180+
set('meta[property="og:description"]', 'content', l.desc);
181+
set('meta[name="twitter:title"]', 'content', l.title);
182+
set('meta[name="twitter:description"]','content', l.desc);
183+
set('h1.display-4', 'textContent', l.heading);
184+
set('#intro-paragraph','textContent', l.para);
162185
})();
163186
</script>
164187
<script src="_framework/blazor.webassembly.js"></script>

0 commit comments

Comments
 (0)