diff --git a/screenshots/html/screenshots.css b/screenshots/html/screenshots.css index 71806dbe6a..5b9fb085f7 100644 --- a/screenshots/html/screenshots.css +++ b/screenshots/html/screenshots.css @@ -50,10 +50,19 @@ img { display: block; } +.missing { + text-align: center; +} + th { background: #0DBD8B; } +br { + display: block; + margin: 4px 0; +} + #form_container { margin: 0px auto; } diff --git a/screenshots/html/script.js b/screenshots/html/script.js index f2dacaca0c..63656dfcb9 100644 --- a/screenshots/html/script.js +++ b/screenshots/html/script.js @@ -15,10 +15,19 @@ */ import { screenshots } from './data.js'; -const header = screenshots[0]; +const dataLanguages = screenshots[0]; +const dataPaths = screenshots[1]; + +// Read default visible languages from the fragment +const fragment = new URLSearchParams(window.location.hash.substring(1)); +// Get the wanted languages from the fragment, or default to "de" and "fr", and ensure "en" is always there +const wantedLanguages = (fragment.get('languages') ? fragment.get('languages').split(',') : ['de', 'fr']) + ["en"]; + +// Map dataLanguages to visibleLanguages, set to 1 if the language is in wantedLanguages, 0 otherwise +let visibleLanguages = dataLanguages.map((language) => wantedLanguages.includes(language) ? 1 : 0); -let visibleLanguages = header.map((x) => 1); let imageWidth = 300; +let showAllScreenshots = false; function addForm() { // Insert the form into the div with id form_container @@ -26,14 +35,14 @@ function addForm() { const languageLabel = document.createElement('label'); languageLabel.textContent = 'Languages:'; form.appendChild(languageLabel); - // Add a check box per entry in the header - for (let i = 0; i < header.length; i++){ + // Add a check box per entry in the dataLanguages + for (let i = 0; i < dataLanguages.length; i++){ const label = document.createElement('label'); - const text = document.createTextNode(header[i]); + const text = document.createTextNode(dataLanguages[i]); const input = document.createElement('input'); input.type = 'checkbox'; input.disabled = i == 0; - input.name = header[i]; + input.name = dataLanguages[i]; input.checked = visibleLanguages[i] == 1; input.onchange = (e) => { if (e.target.checked) { @@ -47,6 +56,8 @@ function addForm() { label.appendChild(text); form.appendChild(label); } + // Add a break line + form.appendChild(document.createElement('br')); // Add a label with the text "Width" const label = document.createElement('label'); label.textContent = 'Screenshots width:'; @@ -64,6 +75,20 @@ function addForm() { addTable(); }; form.appendChild(widthInput); + // Add a label with the text "Show all screenshots" + const label2 = document.createElement('label'); + label2.textContent = 'Show all screenshots:'; + label2.title = 'Show all screenshots, including those with no translated versions.'; + const input2 = document.createElement('input'); + input2.type = 'checkbox'; + input2.name = "test"; + input2.checked = showAllScreenshots; + input2.onchange = (e) => { + showAllScreenshots = e.target.checked; + addTable(); + }; + label2.appendChild(input2); + form.appendChild(label2); document.getElementById('form_container').appendChild(form); } @@ -86,55 +111,76 @@ function addTable() { // First item of screenshots contains the languages // Build the languages row const languagesHeaderRow = document.createElement('tr'); - for (let i = 0; i < header.length; i++) { + for (let languageIndex = 0; languageIndex < dataLanguages.length; languageIndex++) { // Do not add the language if it is hidden - if (visibleLanguages[i] == 0) { + if (visibleLanguages[languageIndex] == 0) { continue; } const th = document.createElement('th'); - th.textContent = header[i]; + th.textContent = dataLanguages[languageIndex]; languagesHeaderRow.appendChild(th); } const numVisibleLanguages = languagesHeaderRow.childElementCount + // Second item contains the paths // Next items are the data var currentHeaderValue = ""; - for (let i = 1; i < screenshots.length; i++) { - // Add a header for row, if different from previous - let name = getNiceName(screenshots[i][0]); - if(name != currentHeaderValue) { - currentHeaderValue = name; - const trHead = document.createElement('tr'); - const tdHead = document.createElement('td'); - tdHead.colSpan = numVisibleLanguages; - tdHead.className = "view-header"; - tdHead.textContent = name; - trHead.appendChild(tdHead); - tbody.appendChild(trHead); - tbody.appendChild(languagesHeaderRow.cloneNode(true)); - } + for (let screenshotIndex = 2; screenshotIndex < screenshots.length; screenshotIndex++) { + let englishFile = screenshots[screenshotIndex][0]; const tr = document.createElement('tr'); - for (let j = 0; j < screenshots[i].length; j++) { - if (visibleLanguages[j] == 0) { + let hasTranslatedFiles = false; + for (let languageIndex = 0; languageIndex < dataLanguages.length; languageIndex++) { + if (visibleLanguages[languageIndex] == 0) { continue; } const td = document.createElement('td'); - let imageFile = screenshots[i][j]; - if (imageFile === '') { - const text = document.createElement('p'); - text.textContent = 'No image'; - td.appendChild(text); - } else { + if (languageIndex == 0) { + const fullFile = `${dataPaths[0]}/${englishFile}.png`; const img = document.createElement('img'); img.className = "screenshot"; - img.src = `../${imageFile}`; - img.title = imageFile; + img.src = `../${fullFile}`; + img.title = fullFile; img.alt = "Missing image"; img.width = imageWidth; td.appendChild(img); + } else { + let hasFile = screenshots[screenshotIndex][languageIndex]; + if (hasFile === 0) { + const text = document.createElement('p'); + text.className = "missing"; + text.textContent = 'No image'; + td.appendChild(text); + } else { + hasTranslatedFiles = true; + // Foreign file is the same as the english file, replacing the language + const foreignFile = englishFile.replace("en]", `${dataLanguages[languageIndex]}]`).replace("_S_", "_T_") + const fullForeignFile = `${dataPaths[languageIndex]}/${foreignFile}.png`; + const img = document.createElement('img'); + img.className = "screenshot"; + img.src = `../${fullForeignFile}`; + img.title = fullForeignFile; + img.alt = "Missing image"; + img.width = imageWidth; + td.appendChild(img); + } } tr.appendChild(td); } - tbody.appendChild(tr); + if (showAllScreenshots || hasTranslatedFiles) { + // Add a header for row, if different from previous + let name = getNiceName(englishFile); + if (name != currentHeaderValue) { + currentHeaderValue = name; + const trHead = document.createElement('tr'); + const tdHead = document.createElement('td'); + tdHead.colSpan = numVisibleLanguages; + tdHead.className = "view-header"; + tdHead.textContent = name; + trHead.appendChild(tdHead); + tbody.appendChild(trHead); + tbody.appendChild(languagesHeaderRow.cloneNode(true)); + } + tbody.appendChild(tr); + } } table.appendChild(thead); table.appendChild(tbody); diff --git a/tools/test/generateAllScreenshots.py b/tools/test/generateAllScreenshots.py index 6ba1ab2b67..cd9d3ec6ab 100755 --- a/tools/test/generateAllScreenshots.py +++ b/tools/test/generateAllScreenshots.py @@ -1,35 +1,53 @@ #!/usr/bin/env python3 +# +# Copyright 2024 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# import os import re import sys from util import compare + # Read all arguments and return a list of them, this are the languages list. def readArguments(): # Return sys.argv without the first argument return sys.argv[1:] + def generateAllScreenshots(languages): # If languages is empty, generate all screenshots if len(languages) == 0: - print("Generating all screenshots...") - os.system("./gradlew recordPaparazziDebug -PallLanguages") + print("Generating all screenshots...") + os.system("./gradlew --stop") + os.system("./gradlew recordPaparazziDebug -PallLanguages") else: - tFile = "tests/uitests/src/test/kotlin/ui/T.kt" - print("Generating screenshots for languages: %s" % languages) - # Patch file T.kt, replace `@TestParameter(value = ["de"]) localeStr: String,` with `@TestParameter(value = ["de", "fr"]) localeStr: String,` - with open(tFile, "r") as file: - data = file.read() - languagesList = ", ".join([f"\"{lang}\"" for lang in languages]) - data = data.replace("@TestParameter(value = [\"de\"]) localeStr: String,", "@TestParameter(value = [%s]) localeStr: String," % languagesList) - with open(tFile, "w") as file: - file.write(data) - os.system("./gradlew recordPaparazziDebug -PallLanguagesNoEnglish") - # Git reset the change on file T.kt - os.system("git checkout HEAD -- %s" % tFile) - - + tFile = "tests/uitests/src/test/kotlin/ui/T.kt" + print("Generating screenshots for languages: %s" % languages) + # Record the languages one by one, else it's getting too slow + for lang in languages: + print("Generating screenshots for language: %s" % lang) + # Patch file T.kt, replace `@TestParameter(value = ["de"]) localeStr: String,` with `@TestParameter(value = []) localeStr: String,` + with open(tFile, "r") as file: + data = file.read() + data = data.replace("@TestParameter(value = [\"de\"]) localeStr: String,", "@TestParameter(value = [\"%s\"]) localeStr: String," % lang) + with open(tFile, "w") as file: + file.write(data) + os.system("./gradlew recordPaparazziDebug -PallLanguagesNoEnglish") + # Git reset the change on file T.kt + os.system("git checkout HEAD -- %s" % tFile) def detectLanguages(): @@ -82,38 +100,44 @@ def detectRecordedLanguages(): # List all the subfolders of the screenshots folder which contains 2 letters, sorted alphabetically return sorted([f for f in os.listdir("screenshots") if len(f) == 2]) + def generateJavascriptFile(): __doc__ = "Generate a javascript file to load the screenshots" print("Generating javascript file...") languages = detectRecordedLanguages() # First item is the list of languages, adding "en" at the beginning data = [["en"] + languages] - # If any translated screenshot exists, keep the file + # Second item is the path of the containing file + data.append(["./tests/uitests/src/test/snapshots/images"] + ["./screenshots/" + l for l in languages]) files = sorted( os.listdir("tests/uitests/src/test/snapshots/images/"), key=lambda file: file[file.find("_", 6):], ) for file in files: - fullFile = "./tests/uitests/src/test/snapshots/images/" + file - dataForFile = [fullFile] - hasAnyTranslatedFile = False + # Continue if file contains "-Night", keep only light screenshots (maybe the night screenshots could be on the second column?) + if "-Night" in file: + continue + dataForFile = [file[:-4]] for l in languages: - translatedFile = "./screenshots/" + l + "/" + file[:3] + "T" + file[4:-7] + l + file[-5:] + simpleFile = file[:3] + "T" + file[4:-7] + l + file[-5:-4] + translatedFile = "./screenshots/" + l + "/" + simpleFile + ".png" if os.path.exists(translatedFile): - hasAnyTranslatedFile = True - dataForFile.append(translatedFile) + dataForFile.append(1) else: - dataForFile.append("") - if hasAnyTranslatedFile: - data.append(dataForFile) + dataForFile.append(0) + data.append(dataForFile) with open("screenshots/html/data.js", "w") as f: f.write("// Generated file, do not edit\n") f.write("export const screenshots = [\n") for line in data: - f.write("[\n") + f.write("[") for item in line: - f.write("\"" + item + "\",\n") + # If item is a string, add quotes + if isinstance(item, str): + f.write("\"" + item + "\",") + else: + f.write(str(item) + ",") f.write("],\n") f.write("];\n") diff --git a/tools/test/generateWorldScreenshots.py b/tools/test/generateWorldScreenshots.py new file mode 100755 index 0000000000..8c68ed8266 --- /dev/null +++ b/tools/test/generateWorldScreenshots.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# +# Copyright 2024 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os + +def detectAllExistingTranslations(): + # Read all the folder in "libraries/ui-strings/src/main/res" + folders = os.listdir("libraries/ui-strings/src/main/res") + # Remove the "values" folder + folders.remove("values") + # Map to keep only the language code + folders = list(map(lambda folder: folder[7:], folders)) + # Map to keep only the string before the "-" + folders = list(map(lambda folder: folder.split("-")[0], folders)) + # Remove duplicates + folders = list(set(folders)) + return folders + + +def main(): + languages = detectAllExistingTranslations() + print ("Will record the screenshots for those languages: %s" % languages) + # Run the python script "generateAllScreenshots.py" with the detected languages + os.system("./tools/test/generateAllScreenshots.py %s" % " ".join(languages)) + +main()