From 3ff451aa1da0f976435084b87a8a4de79d664333 Mon Sep 17 00:00:00 2001 From: Axel Thornberg Date: Thu, 10 Oct 2024 11:17:22 +0200 Subject: [PATCH] FInished fixing tests and reworking automatic testing --- .github/workflows/automated-tests.yml | 22 +++++-- .github/workflows/w3c-validation.yml | 70 ---------------------- database.env | 2 - helpers/handlebars.js | 37 ++++++------ public/css/no-script-hamburger-menu.css | 9 --- public/css/no-script-locale-dropdown.css | 17 ------ public/css/no-script-location-dropdown.css | 17 ------ server.js | 10 ++++ tests/template.py | 1 - tests/test_dynamic_open_hours_display.py | 19 ------ tests/test_essential_info.py | 2 - tests/test_hamburger_menu.py | 27 ++++----- tests/test_holidays.py | 27 ++------- tests/test_misc.py | 1 - tests/test_our_cars.py | 1 - tests/test_zip_codes.py | 12 ++-- tests/utils.py | 14 +++++ views/index.hbs | 6 +- 18 files changed, 81 insertions(+), 213 deletions(-) delete mode 100644 .github/workflows/w3c-validation.yml delete mode 100644 database.env delete mode 100644 public/css/no-script-hamburger-menu.css delete mode 100644 public/css/no-script-locale-dropdown.css delete mode 100644 public/css/no-script-location-dropdown.css diff --git a/.github/workflows/automated-tests.yml b/.github/workflows/automated-tests.yml index 4f8f210..8b41f59 100644 --- a/.github/workflows/automated-tests.yml +++ b/.github/workflows/automated-tests.yml @@ -20,8 +20,7 @@ name: Index Page Automated Content Tests on: push: branches: - - main - - live + - "**" jobs: test: @@ -36,12 +35,25 @@ jobs: with: python-version: "3.12.5" + - name: "Install Node.js" + uses: actions/setup-node@v4 + with: + node-version: "latest" + - name: Install dependencies run: | - pip install pytest-playwright - pip install playwright - pip install pytest-json-report + pip install pytest-playwright playwright pytest-json-report python-dotenv playwright install + npm install + + - name: Create a .env file dynamically from environment variables + run: | + printenv | grep ^SECRET_ > .env + shell: bash + + - name: Run server + run: | + npm run start - name: Run tests with pytest-playwright run: | diff --git a/.github/workflows/w3c-validation.yml b/.github/workflows/w3c-validation.yml deleted file mode 100644 index c9d427a..0000000 --- a/.github/workflows/w3c-validation.yml +++ /dev/null @@ -1,70 +0,0 @@ -# Name: W3C HTML -# -# Description: This GitHub Actions workflow performs HTML using the W3C Validator API. It runs on the 'main', 'gh-pages', and 'validation-testing' branches whenever a push event occurs. -# -# Jobs: -# - validate: -# - Description: This job runs on the latest version of Ubuntu and performs the following steps: -# 1. Checkout code from the repository. -# 2. Set up Python environment. -# 3. Install dependencies (requests library). -# 4. Validate HTML files using the W3C Validator API. -# 6. Upload validation results as artifacts. -# 7. Fail the workflow if there were validation issues. -name: W3C HTML Validation - -on: - push: - branches: - - main - - live - -jobs: - validate: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.12.5" - - - name: Install dependencies - run: | - pip install requests - - - name: Validate HTML with W3C Validator API - run: | - mkdir -p validation_results - validation_failed=0 - for file in $(find . -name "*.html"); do - echo "Validating $file" - response=$(curl -s -F "out=json" -F "content=@$file" https://validator.w3.org/nu/?parser=html5) - output_file="validation_results/w3c-validation-$(basename $file .html).json" - echo "$response" > "$output_file" - errors=$(echo "$response" | python3 -c "import sys, json; data = json.load(sys.stdin); print(len([msg for msg in data['messages'] if msg['type'] in ['error', 'warning']]))") - if [ "$errors" -gt 0 ]; then - echo "Validation issues found in $file" - validation_failed=1 - fi - done - echo "$validation_failed" > validation_results/validation_failed_flag.txt - - - name: Upload validation results - uses: actions/upload-artifact@v4 - with: - name: validation-results - path: validation_results - - - name: Fail if validation issues found - run: | - validation_failed=$(cat validation_results/validation_failed_flag.txt) - if [ "$validation_failed" -eq 1 ]; then - echo "Validation issues found. Failing the workflow." - exit 1 - else - echo "Validation successful." - fi diff --git a/database.env b/database.env deleted file mode 100644 index 82226ec..0000000 --- a/database.env +++ /dev/null @@ -1,2 +0,0 @@ -URL_PROD=https://cars-prod.ntbbiluthyrning.workers.dev/api/cars -URL_DEV=https://cars.ntbbiluthyrning.workers.dev/api/cars \ No newline at end of file diff --git a/helpers/handlebars.js b/helpers/handlebars.js index aec46bb..d427e69 100644 --- a/helpers/handlebars.js +++ b/helpers/handlebars.js @@ -85,60 +85,57 @@ const currentStatus = (location, lang, debugTime) => { openHours = location.open_hours; } + const openHoursToday = openHours.find((element) => element.index === now.getDay()); + const tempDate = new Date(now); // Get the weekday object for the current day - let openHoursToday = openHours.find((element) => element.index === tempDate.getDay()); + let nextOpenHours = openHours.find((element) => element.index === tempDate.getDay()); // Increment tempDate by 1 day until a day is found where the store is open while ( closedDates.map((element) => element.date).includes(`${tempDate.getMonth().toString().padStart(2, "0")}${tempDate.getDate().toString().padStart(2, "0")}`) || // Check if it is a holiday - openHoursToday.from_hour === null || // Check if it is a weekday that is normally not open - openHoursToday.to_hour === null || // Check if it is a weekday that is normally not open - openHoursToday.from_minute === null || // Check if it is a weekday that is normally not open - openHoursToday.to_minute === null || // Check if it is a weekday that is normally not open - (tempDate.getTime() === now.getTime() && (now.getHours() > openHoursToday.to_hour || (now.getHours() === openHoursToday.to_hour && now.getMinutes() >= openHoursToday.to_minute))) // Check if it is the first iteration and the store has closed for the day + nextOpenHours.from_hour === null || // Check if it is a weekday that is normally not open + nextOpenHours.to_hour === null || // Check if it is a weekday that is normally not open + nextOpenHours.from_minute === null || // Check if it is a weekday that is normally not open + nextOpenHours.to_minute === null || // Check if it is a weekday that is normally not open + (tempDate.getTime() === now.getTime() && (now.getHours() > nextOpenHours.to_hour || (now.getHours() === nextOpenHours.to_hour && now.getMinutes() >= nextOpenHours.to_minute))) // Check if it is the first iteration and the store has closed for the day ) { // Increment tempDate by 1 day tempDate.setDate(tempDate.getDate() + 1); // Get the weekday object for the next day - openHoursToday = openHours.find((element) => element.index === tempDate.getDay()); + nextOpenHours = openHours.find((element) => element.index === tempDate.getDay()); } - - // Get the weekday object for the next open day - const nextOpenDayObject = openHours.find((element) => element.index === tempDate.getDay()); - // Formats the current time to "MMDD" const dateString = `${now.getMonth().toString().padStart(2, "0")}${now.getDate().toString().padStart(2, "0")}`; // Check if it is a holiday if (closedDates.map((element) => element.date).includes(dateString)) { const holiday = closedDates.find((element) => element.date === dateString); - const nextOpenDay = lang[capitalize_weekdays] ? lang[nextOpenDayObject.day] : lang[nextOpenDayObject.day].toLowerCase(); - const time = `${nextOpenDayObject.from_hour.toString().padStart(2, "0")}:${nextOpenDayObject.from_minute.toString().padStart(2, "0")}`; + const nextOpenDay = lang.capitalize_weekdays ? lang[nextOpenHours.day] : lang[nextOpenHours.day].toLowerCase(); + const time = `${nextOpenHours.from_hour.toString().padStart(2, "0")}:${nextOpenHours.from_minute.toString().padStart(2, "0")}`; return lang.closed_now_holiday.replace("${holiday}", holiday.name).replace("${next_open_day}", nextOpenDay).replace("${time}", time); } // Check if it is a weekday that is normally not open if (openHoursToday.from_hour === null || openHoursToday.to_hour === null || openHoursToday.from_minute === null || openHoursToday.to_minute === null) { - const nextOpenDay = lang[capitalize_weekdays] ? lang[nextOpenDayObject.day] : lang[nextOpenDayObject.day].toLowerCase(); - const time = `${nextOpenDayObject.from_hour.toString().padStart(2, "0")}:${nextOpenDayObject.from_minute.toString().padStart(2, "0")}`; + const nextOpenDay = lang.capitalize_weekdays ? lang[nextOpenHours.day] : lang[nextOpenHours.day].toLowerCase(); + const time = `${nextOpenHours.from_hour.toString().padStart(2, "0")}:${nextOpenHours.from_minute.toString().padStart(2, "0")}`; return lang.after_hours.replace("${next_open_day}", nextOpenDay).replace("${time}", time); } // Check if the store has not opened yet for the day if (now.getHours() < openHoursToday.from_hour || (now.getHours() === openHoursToday.from_hour && now.getMinutes() < openHoursToday.from_minute)) { - const time = `${openHoursToday.from_hour.toString().padStart(2, "0")}:${openHoursToday.from_minute.toString().padStart(2, "0")}`; + const time = `${nextOpenHours.from_hour.toString().padStart(2, "0")}:${nextOpenHours.from_minute.toString().padStart(2, "0")}`; return lang.not_open_yet.replace("${time}", time); } // Check if the store is currently open if (now.getHours() < openHoursToday.to_hour || (now.getHours() === openHoursToday.to_hour && now.getMinutes() < openHoursToday.to_minute)) { - const time = `${openHoursToday.to_hour.toString().padStart(2, "0")}:${openHoursToday.to_minute.toString().padStart(2, "0")}`; + const time = `${nextOpenHours.to_hour.toString().padStart(2, "0")}:${nextOpenHours.to_minute.toString().padStart(2, "0")}`; return lang.open_now.replace("${time}", time); } - // Check if the store has closed for the day - const nextOpenDay = lang[capitalize_weekdays] ? lang[nextOpenDayObject.day] : lang[nextOpenDayObject.day].toLowerCase(); - const time = `${nextOpenDayObject.from_hour.toString().padStart(2, "0")}:${nextOpenDayObject.from_minute.toString().padStart(2, "0")}`; + const nextOpenDay = lang.capitalize_weekdays ? lang[nextOpenHours.day] : lang[nextOpenHours.day].toLowerCase(); + const time = `${nextOpenHours.from_hour.toString().padStart(2, "0")}:${nextOpenHours.from_minute.toString().padStart(2, "0")}`; return lang.after_hours.replace("${next_open_day}", nextOpenDay).replace("${time}", time); }; diff --git a/public/css/no-script-hamburger-menu.css b/public/css/no-script-hamburger-menu.css deleted file mode 100644 index 40a7647..0000000 --- a/public/css/no-script-hamburger-menu.css +++ /dev/null @@ -1,9 +0,0 @@ -body { - & > header { - & > .hamburger-menu-wrapper { - & #hamburger-menu-toggle:checked + .hamburger-menu + .dropdown-menu { - display: flex; - } - } - } -} \ No newline at end of file diff --git a/public/css/no-script-locale-dropdown.css b/public/css/no-script-locale-dropdown.css deleted file mode 100644 index 3adc704..0000000 --- a/public/css/no-script-locale-dropdown.css +++ /dev/null @@ -1,17 +0,0 @@ -body { - &>header { - &>.redirect-dropdowns { - & .redirect-dropdown { - &:focus-within #locale-dropdown-content { - visibility: visible; - opacity: 1; - margin-top: 5px; - z-index: 1; - } - &:focus-within #locale-dropdown-arrow { - transform: rotate(180deg); - } - } - } - } -} \ No newline at end of file diff --git a/public/css/no-script-location-dropdown.css b/public/css/no-script-location-dropdown.css deleted file mode 100644 index 32ecc41..0000000 --- a/public/css/no-script-location-dropdown.css +++ /dev/null @@ -1,17 +0,0 @@ -body { - &>header { - &>.redirect-dropdowns { - & .redirect-dropdown { - &:focus-within #location-dropdown-content { - visibility: visible; - opacity: 1; - margin-top: 5px; - z-index: 1; - } - &:focus-within #location-dropdown-arrow { - transform: rotate(180deg); - } - } - } - } -} \ No newline at end of file diff --git a/server.js b/server.js index 11590cf..ff8a921 100644 --- a/server.js +++ b/server.js @@ -55,12 +55,22 @@ app.get("/", (req, res) => { app.post("/POST/language", (req, res) => { const { language, route } = req.body; req.session.language = language; + req.session.save((err) => { + if (err) { + console.error(err); + } + }); res.redirect(`/${route}`); }); app.post("/POST/location", (req, res) => { const { location, route } = req.body; req.session.location = location; + req.session.save((err) => { + if (err) { + console.error(err); + } + }); res.redirect(`/${route}`); }); diff --git a/tests/template.py b/tests/template.py index 8d7585f..8fd2508 100644 --- a/tests/template.py +++ b/tests/template.py @@ -1,4 +1,3 @@ -import unittest from utils import * diff --git a/tests/test_dynamic_open_hours_display.py b/tests/test_dynamic_open_hours_display.py index cb809fd..5e1da60 100644 --- a/tests/test_dynamic_open_hours_display.py +++ b/tests/test_dynamic_open_hours_display.py @@ -1,7 +1,3 @@ -import unittest -import datetime -import dotenv -from os import path from utils import * @@ -16,21 +12,6 @@ def testSelfName(self) -> None: if self.__class__.__name__ == "TestName": self.fail("Test class name is not correct") - # helpers - def setPageTimeTo(self, year: int, month: int, day: int, hour: int, minute: int) -> None: - pass - - def setAndTestTime(self, year: int, month: int, day: int, hour: int, minute: int, expected: list[str]) -> None: - # Convert the given time to unix time - time = str(int(datetime.datetime(year, month, day, hour, minute, tzinfo=datetime.datetime.now().astimezone().tzinfo).timestamp()) * 1000) - debugKey = dotenv.get_key(path.join(path.dirname(__file__), "..", ".env"), "DEBUG_KEY") - self.page.goto(f"http://localhost:4000/?debugTime={time}&debugKey={debugKey}") - self.page.wait_for_selector("#checkJsCompleted", state="attached") - self.assertInAllTextContent(expected) - - def currentYear(self) -> int: - return datetime.datetime.now().year - # tests def testWeekdays(self) -> None: # monday before open. we expect the open time to show diff --git a/tests/test_essential_info.py b/tests/test_essential_info.py index 1437758..0549441 100644 --- a/tests/test_essential_info.py +++ b/tests/test_essential_info.py @@ -1,6 +1,4 @@ -import unittest from utils import * -import datetime class TestEssentialInfo(TemplateTest): diff --git a/tests/test_hamburger_menu.py b/tests/test_hamburger_menu.py index a8c51bc..d0f4cbe 100644 --- a/tests/test_hamburger_menu.py +++ b/tests/test_hamburger_menu.py @@ -1,4 +1,3 @@ -import unittest from utils import * @@ -21,25 +20,25 @@ def testSelfName(self) -> None: def testHamburgerMenuDropdown(self) -> None: # Check that the dropdown menu switches between visible and hidden when the hamburger menu button is clicked - hamburgerMenuButton = self.page.query_selector(".hamburger-menu") + hamburgerMenuButton = self.page.locator("header > section > button") - self.assertFalse(self.page.is_visible(".dropdown-menu")) + self.assertFalse(self.page.is_visible("header > section > nav")) - hamburgerMenuButton.click() + hamburgerMenuButton.hover() - self.assertTrue(self.page.is_visible(".dropdown-menu")) + self.assertTrue(self.page.is_visible("header > section > nav")) - hamburgerMenuButton.click() + self.page.mouse.move(0, 812) - self.assertFalse(self.page.is_visible(".dropdown-menu")) + self.assertFalse(self.page.is_visible("header > section > nav")) def testHamburgerMenuLinks(self) -> None: # Check that the hamburger menu links are correct - hamburgerMenuButton = self.page.query_selector(".hamburger-menu") - hamburgerMenuLinks = self.page.query_selector_all(".dropdown-menu a") + hamburgerMenuButton = self.page.locator("header > section > button") + hamburgerMenuLinks = self.page.locator("header > section > nav > a").all() - hamburgerMenuButton.click() + hamburgerMenuButton.hover() self.assertEqual(len(hamburgerMenuLinks), 4) @@ -50,11 +49,11 @@ def testHamburgerMenuLinks(self) -> None: def testHamburgerMenuLinkClick(self) -> None: # Check that the hamburger menu links navigate to the correct sections - hamburgerMenuButton = self.page.query_selector(".hamburger-menu") - hamburgerMenuLinks = self.page.query_selector_all(".dropdown-menu a") + hamburgerMenuButton = self.page.locator("header > section > button") + hamburgerMenuLinks = self.page.locator("header > section > nav > a").all() # Open the hamburger menu - hamburgerMenuButton.click() + hamburgerMenuButton.hover() # Click the first link and verify the section title is in the viewport hamburgerMenuLinks[0].click() @@ -79,7 +78,7 @@ def testHamburgerMenuLinkClick(self) -> None: self.assertTrue(isInViewport) # Open the hamburger menu again - hamburgerMenuButton.click() + hamburgerMenuButton.hover() # Click the fourth link and verify the section title is in the viewport hamburgerMenuLinks[3].click() diff --git a/tests/test_holidays.py b/tests/test_holidays.py index bf3c121..1742b4a 100644 --- a/tests/test_holidays.py +++ b/tests/test_holidays.py @@ -1,6 +1,3 @@ -import unittest -import re -import datetime from utils import * @@ -16,22 +13,9 @@ def testSelfName(self) -> None: self.fail("Test class name is not correct") # helpers - def setPageTimeTo(self, year: int, month: int, day: int, hour: int, minute: int) -> None: - self.page.evaluate( - f""" - now.setFullYear({year}, {month - 1}, {day}); - now.setHours({hour}); - now.setMinutes({minute}); - refreshDynamicOpenStatus(); - """ - ) - - def setAndTestTime(self, year: int, month: int, day: int, hour: int, minute: int, expected: list[str]) -> None: - self.setPageTimeTo(year, month, day, hour, minute) - self.assertInAllTextContent(expected) def setTimeAndGetClosedDatesTable(self, year: int, month: int, day: int, hour: int, minute: int) -> list[list[str]]: - self.setPageTimeTo(year, month, day, hour, minute) + self.setTime(year, month, day, hour, minute) closedDatesTable = self.page.query_selector(".closed-dates-table").inner_html().split("") @@ -45,9 +29,6 @@ def setTimeAndGetClosedDatesTable(self, year: int, month: int, day: int, hour: i return closedDatesTable - def currentYear(self) -> int: - return datetime.datetime.now().year - # tests def testHolidays(self) -> None: self.setAndTestTime(2024, 12, 24, 12, 37, ["Julafton", "fredag", "10:00"]) @@ -61,7 +42,7 @@ def testHolidays(self) -> None: self.setAndTestTime(2025, 6, 6, 11, 37, ["Nationaldagen", "lördag", "11:00"]) def testHolidaySortingChristmasEve(self) -> None: - closedDatesTable = self.setTimeAndGetClosedDatesTable(self.currentYear(), 12, 24, 19, 16) + closedDatesTable = self.setTimeAndGetClosedDatesTable(2024, 12, 24, 19, 16) self.assertEqual(closedDatesTable[0][0], "24 december") self.assertEqual(closedDatesTable[0][1], "Julafton") self.assertEqual(closedDatesTable[0][2], "Stängt") @@ -75,7 +56,7 @@ def testHolidaySortingChristmasEve(self) -> None: self.assertEqual(closedDatesTable[7][2], "Stängt") def testHolidaySortingNewYearsDay(self) -> None: - closedDatesTable = self.setTimeAndGetClosedDatesTable(self.currentYear()+1, 1, 1, 19, 16) + closedDatesTable = self.setTimeAndGetClosedDatesTable(2025, 1, 1, 19, 16) self.assertEqual(closedDatesTable[0][0], "1 januari") self.assertEqual(closedDatesTable[0][1], "Nyårsdagen") self.assertEqual(closedDatesTable[0][2], "Stängt") @@ -89,7 +70,7 @@ def testHolidaySortingNewYearsDay(self) -> None: self.assertEqual(closedDatesTable[6][2], "Stängt") def testHolidaySortingNationalDay(self) -> None: - closedDatesTable = self.setTimeAndGetClosedDatesTable(self.currentYear()+1, 6, 6, 19, 16) + closedDatesTable = self.setTimeAndGetClosedDatesTable(2025, 6, 6, 19, 16) self.assertEqual(closedDatesTable[0][0], "6 juni") self.assertEqual(closedDatesTable[0][1], "Nationaldagen") self.assertEqual(closedDatesTable[0][2], "Stängt") diff --git a/tests/test_misc.py b/tests/test_misc.py index 7b3d7de..936e241 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1,4 +1,3 @@ -import unittest from utils import * diff --git a/tests/test_our_cars.py b/tests/test_our_cars.py index 637757f..41afbc8 100644 --- a/tests/test_our_cars.py +++ b/tests/test_our_cars.py @@ -1,4 +1,3 @@ -import unittest from utils import * diff --git a/tests/test_zip_codes.py b/tests/test_zip_codes.py index 1b1020a..b8b634e 100644 --- a/tests/test_zip_codes.py +++ b/tests/test_zip_codes.py @@ -1,5 +1,3 @@ -import unittest -import time from utils import * @@ -15,11 +13,11 @@ def testSelfName(self) -> None: self.fail("Test class name is not correct") # Helper functions - def submitAndTestZIPCode(self, zip_code: str, match: str) -> None: - zip_input = self.page.query_selector("#zip-input") - zip_button = self.page.query_selector("#zip-button") - zip_input.fill(zip_code) - zip_button.click() + def submitAndTestZIPCode(self, zipCode: str, match: str) -> None: + zipInput = self.page.locator("#zip-input") + zipButton = self.page.locator("#zip-button") + zipInput.fill(zipCode) + zipButton.click() time.sleep(0.2) self.assertInTextContent(match) diff --git a/tests/utils.py b/tests/utils.py index b782051..e96ae18 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,4 +1,8 @@ import unittest +import datetime +import dotenv +import time +import re from playwright.sync_api import sync_playwright from os import path @@ -38,6 +42,16 @@ def tearDown(self) -> None: # Helper functions # + def setTime(self, year: int, month: int, day: int, hour: int, minute: int) -> None: + time = str(int(datetime.datetime(year, month, day, hour, minute, tzinfo=datetime.datetime.now().astimezone().tzinfo).timestamp()) * 1000) + debugKey = dotenv.get_key(path.join(path.dirname(__file__), "..", ".env"), "DEBUG_KEY") + self.page.goto(f"http://localhost:4000/?debugTime={time}&debugKey={debugKey}") + self.page.wait_for_selector("#checkJsCompleted", state="attached") + + def setAndTestTime(self, year: int, month: int, day: int, hour: int, minute: int, expected: list[str]) -> None: + self.setTime(year, month, day, hour, minute) + self.assertInAllTextContent(expected) + # run assertIn for every string in the list def assertInAll(self, matches: list[str]) -> None: for match in matches: diff --git a/views/index.hbs b/views/index.hbs index bf49685..15d6eea 100644 --- a/views/index.hbs +++ b/views/index.hbs @@ -24,7 +24,6 @@ - @@ -179,10 +178,7 @@ -