From ecb2a0e736332d87e601d9d8d86dfca66740e460 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Apr 2026 23:06:37 +0200 Subject: [PATCH] Add Docker `TAGLINE` env customization for modern frontend slogan (#794) * Initial plan * Add Docker TAGLINE env customization for modern UI slogan Agent-Logs-Url: https://github.com/librespeed/speedtest/sessions/e1da6e0e-5194-453d-bffb-961ed782e215 Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * Refine TAGLINE replacement and expand E2E coverage Agent-Logs-Url: https://github.com/librespeed/speedtest/sessions/e1da6e0e-5194-453d-bffb-961ed782e215 Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> * Fix TAGLINE sed delimiter and add apostrophe tagline E2E test - Switch TAGLINE sed from '#' to '/' delimiter so html_escape'd apostrophes (') don't break the sed expression - Add standalone-apostrophe Docker service with TAGLINE="It'd rather be fast!" - Add standaloneApostrophe URL (port 18186) to env.js - Add E2E test asserting the apostrophe tagline renders correctly Agent-Logs-Url: https://github.com/librespeed/speedtest/sessions/ebe265a8-4b1e-49b5-959a-66133ea0ab3a Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sstidl <12804296+sstidl@users.noreply.github.com> --- Dockerfile | 1 + Dockerfile.alpine | 1 + doc_docker.md | 2 ++ docker/entrypoint.sh | 9 +++++++++ tests/docker-compose-playwright.yml | 13 +++++++++++++ tests/e2e/helpers/env.js | 1 + tests/e2e/modes.spec.js | 3 +++ tests/e2e/title-special-chars.spec.js | 10 +++++++++- 8 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7af5bb5..f740129 100755 --- a/Dockerfile +++ b/Dockerfile @@ -32,6 +32,7 @@ COPY docker/entrypoint.sh / # Prepare default environment variables ENV TITLE=LibreSpeed +ENV TAGLINE="No Flash, No Java, No Websockets, No Bullsh*t" ENV MODE=standalone ENV PASSWORD=password ENV TELEMETRY=false diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 8f6d989..1a99be9 100755 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -46,6 +46,7 @@ COPY docker/entrypoint.sh / # Prepare default environment variables ENV TITLE=LibreSpeed +ENV TAGLINE="No Flash, No Java, No Websockets, No Bullsh*t" ENV MODE=standalone ENV PASSWORD=password ENV TELEMETRY=false diff --git a/doc_docker.md b/doc_docker.md index 5fc0b38..cff25d7 100755 --- a/doc_docker.md +++ b/doc_docker.md @@ -33,6 +33,7 @@ services: environment: MODE: standalone #TITLE: "LibreSpeed" + #TAGLINE: "No Flash, No Java, No Websockets, No Bullsh*t" #TELEMETRY: "false" #ENABLE_ID_OBFUSCATION: "false" #REDACT_IP_ADDRESSES: "false" @@ -57,6 +58,7 @@ The test can be accessed on port 80. Here's a list of additional environment variables available in this mode: * __`TITLE`__: Title of your speed test. Default value: `LibreSpeed` +* __`TAGLINE`__: Slogan shown below the heading on the modern frontend (`index-modern.html`). Default value: `No Flash, No Java, No Websockets, No Bullsh*t` * __`USE_NEW_DESIGN`__: When set to `true`, enables the new modern frontend design. When set to `false` (default), uses the classic design. The design can also be switched using URL parameters (`?design=new` or `?design=old`). Default value: `false` * __`SERVER_LIST_URL`__: When set, both frontend designs load their server list from this URL instead of the generated or mounted `server-list.json`. This is useful if you want the containerized frontend to consume a remote shared server list. * __`TELEMETRY`__: Whether to enable telemetry or not. If enabled, you maybe want your data to be persisted. See below. Default value: `false` diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index bbf7bd9..7ede3a7 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -108,6 +108,15 @@ if [[ "$MODE" == "frontend" || "$MODE" == "dual" || "$MODE" == "standalone" ]]; sed -i "s/LibreSpeed<\\/title>/<title>$TITLE_ESCAPED<\\/title>/g" /var/www/html/index.html sed -i "s/<title>LibreSpeed - Free and Open Source Speedtest<\\/title>/<title>$TITLE_ESCAPED - Free and Open Source Speedtest<\\/title>/g; s/<h1>Free and Open Source Speedtest\\.<\\/h1>/<h1>$TITLE_ESCAPED<\\/h1>/g" /var/www/html/index-modern.html fi + + # Replace modern page tagline if TAGLINE is set + if [ -n "$TAGLINE" ]; then + TAGLINE_ONE_LINE=${TAGLINE//$'\r'/} + TAGLINE_ONE_LINE=${TAGLINE_ONE_LINE//$'\n'/ } + TAGLINE_HTML_ESCAPED=$(html_escape "$TAGLINE_ONE_LINE") + TAGLINE_ESCAPED=$(sed_escape "$TAGLINE_HTML_ESCAPED") + sed -i "s/<p class=\"tagline\">No Flash, No Java, No Websockets, No Bullsh\\*t<\\/p>/<p class=\"tagline\">$TAGLINE_ESCAPED<\\/p>/g" /var/www/html/index-modern.html + fi # Support legacy EMAIL env var as fallback for GDPR_EMAIL if [ -z "$GDPR_EMAIL" ] && [ ! -z "$EMAIL" ]; then diff --git a/tests/docker-compose-playwright.yml b/tests/docker-compose-playwright.yml index 2575d26..b641ddc 100644 --- a/tests/docker-compose-playwright.yml +++ b/tests/docker-compose-playwright.yml @@ -27,9 +27,22 @@ services: - WEBPORT=8080 - USE_NEW_DESIGN=true - 'TITLE=Grüße "Tempo" ''Österreich''' + - 'TAGLINE=No "Flash", <No Java>, No Websockets & No Bullsh*t' ports: - "18185:8080" + standalone-apostrophe: + build: + context: .. + dockerfile: Dockerfile + environment: + - MODE=standalone + - WEBPORT=8080 + - USE_NEW_DESIGN=true + - "TAGLINE=It'd rather be fast!" + ports: + - "18186:8080" + backend: build: context: .. diff --git a/tests/e2e/helpers/env.js b/tests/e2e/helpers/env.js index 985b3f8..51a21c9 100644 --- a/tests/e2e/helpers/env.js +++ b/tests/e2e/helpers/env.js @@ -4,6 +4,7 @@ const baseUrls = { frontend: 'http://127.0.0.1:18182', dual: 'http://127.0.0.1:18183', standaloneNew: 'http://127.0.0.1:18185', + standaloneApostrophe: 'http://127.0.0.1:18186', }; module.exports = { diff --git a/tests/e2e/modes.spec.js b/tests/e2e/modes.spec.js index 23259f7..b136bbe 100644 --- a/tests/e2e/modes.spec.js +++ b/tests/e2e/modes.spec.js @@ -2,6 +2,8 @@ const { test, expect } = require('@playwright/test'); const { baseUrls } = require('./helpers/env'); const { modernStartButton } = require('./helpers/ui'); +const defaultTagline = 'No Flash, No Java, No Websockets, No Bullsh*t'; + test.describe('Runtime mode smoke coverage', () => { test('standalone exposes UI and local backend endpoints', async ({ page, request }) => { const root = await request.get(`${baseUrls.standalone}/`); @@ -18,6 +20,7 @@ test.describe('Runtime mode smoke coverage', () => { await page.goto(`${baseUrls.standalone}/index-modern.html`); await expect(modernStartButton(page)).toBeVisible(); + await expect(page.locator('main > p.tagline')).toHaveText(defaultTagline); }); test('backend exposes only local backend contract endpoints', async ({ request }) => { diff --git a/tests/e2e/title-special-chars.spec.js b/tests/e2e/title-special-chars.spec.js index be0f054..a25c8b6 100644 --- a/tests/e2e/title-special-chars.spec.js +++ b/tests/e2e/title-special-chars.spec.js @@ -2,16 +2,24 @@ const { test, expect } = require('@playwright/test'); const { baseUrls } = require('./helpers/env'); const specialTitle = 'Grüße "Tempo" \'Österreich\''; +const specialTagline = 'No "Flash", <No Java>, No Websockets & No Bullsh*t'; +const apostropheTagline = "It'd rather be fast!"; -test.describe('TITLE special characters', () => { +test.describe('TITLE and TAGLINE special characters', () => { test('modern page title supports umlauts and quotes', async ({ page }) => { await page.goto(`${baseUrls.standaloneNew}/index-modern.html`); await expect(page).toHaveTitle(`${specialTitle} - Free and Open Source Speedtest`); await expect(page.locator('main > h1')).toHaveText(specialTitle); + await expect(page.locator('main > p.tagline')).toHaveText(specialTagline); }); test('classic heading supports umlauts and quotes', async ({ page }) => { await page.goto(`${baseUrls.standaloneNew}/index-classic.html`); await expect(page.locator('h1').first()).toHaveText(specialTitle); }); + + test('modern page tagline renders apostrophe correctly', async ({ page }) => { + await page.goto(`${baseUrls.standaloneApostrophe}/index-modern.html`); + await expect(page.locator('main > p.tagline')).toHaveText(apostropheTagline); + }); });