Skip to content

Commit 1d6ea49

Browse files
committed
merge main
2 parents bf32cc3 + fa0aab0 commit 1d6ea49

File tree

126 files changed

+3350
-625
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+3350
-625
lines changed

.github/workflows/smoke.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
name: Mac and Windows Smoke
3+
4+
run-name: ${{ github.actor }} is running smoke tests
5+
on:
6+
pull_request:
7+
8+
jobs:
9+
Smoke-Windows:
10+
runs-on: windows-latest
11+
steps:
12+
- name: Checkout repository
13+
uses: actions/checkout@v4
14+
- name: Set up Python 3.11
15+
uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.11"
18+
- name: Install dependencies
19+
run: |
20+
mkdir -p artifacts;
21+
pip3 install 'pipenv==2023.11.15';
22+
pip3 install 'ruff>=0.4.8,<0.5';
23+
bash collect_executables.sh;
24+
sleep 3;
25+
rm ./pyproject.toml;
26+
mv ./ci_pyproject.toml ./pyproject.toml;
27+
pipenv install;
28+
- name: Run Smoke Tests in Win
29+
run: |
30+
pipenv run pytest --fx-executable ./firefox/firefox -n 4 .
31+
$env:TEST_EXIT_CODE = $LASTEXITCODE
32+
mv artifacts artifacts-win || true
33+
exit $env:TEST_EXIT_CODE
34+
- name: Upload artifacts
35+
if: always()
36+
uses: actions/upload-artifact@v4
37+
with:
38+
name: artifacts-win
39+
path: artifacts-win
40+
Smoke-MacOS:
41+
runs-on: macos-latest
42+
steps:
43+
- name: Checkout repository
44+
uses: actions/checkout@v4
45+
- name: Set up Python 3.11
46+
uses: actions/setup-python@v5
47+
with:
48+
python-version: "3.11"
49+
- name: Install dependencies
50+
run: |
51+
mkdir -p artifacts;
52+
pip3 install 'pipenv==2023.11.15';
53+
pip3 install 'ruff>=0.4.8,<0.5';
54+
bash collect_executables.sh;
55+
sleep 3;
56+
rm ./pyproject.toml;
57+
mv ./ci_pyproject.toml ./pyproject.toml;
58+
pipenv install;
59+
- name: Run Smoke Tests in MacOS
60+
run: |
61+
pipenv run pytest --fx-executable ./firefox/firefox -s -n 4 . || TEST_EXIT_CODE=$?
62+
mv artifacts artifacts-mac || true
63+
exit $TEST_EXIT_CODE
64+
- name: Upload artifacts
65+
if: always()
66+
uses: actions/upload-artifact@v4
67+
with:
68+
name: artifacts-mac
69+
path: artifacts-mac

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
results.txt
33
err_log.txt
44
**/report.html
5+
**/result.xml
6+
**/report.xml
57
assets/
68
html_dump/
79
/Test Results - .html
@@ -13,6 +15,7 @@ env
1315
*.zip
1416
*.gz
1517
*.7z
18+
*.xml
1619
test.sh
1720
file[0-9]*.txt
1821

.taskcluster.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ tasks:
183183
${normProjectUpper}_HEAD_REF: '${short_head_ref}'
184184
${normProjectUpper}_HEAD_REV: '${head_sha}'
185185
${normProjectUpper}_REPOSITORY_TYPE: git
186-
${normProjectUpper}_PIP_REQUIREMENTS: taskcluster/requirements.txt
187186
REPOSITORIES:
188187
$json:
189188
${normProject}: ${normProject}
@@ -203,7 +202,7 @@ tasks:
203202
features:
204203
taskclusterProxy: true
205204

206-
image: mozillareleases/taskgraph:decision-c4ac262880970ca484105929e02dd12f00214d5f2603ab6ce4c0d17de5cd0280@sha256:2dd667994aa13fccdcdcede85c570a5eb4a5247e42875a9e95a135ef774ee469
205+
image: mozillareleases/taskgraph:decision-v9.0.0@sha256:e56c7e5cd467c2ce497c344b358f68cc84a4f73f3422e507a97b397d4e617fbd
207206
maxRunTime: 1800
208207

209208
command:
@@ -220,11 +219,11 @@ tasks:
220219
cd /builds/worker/checkouts/src &&
221220
ln -s /builds/worker/artifacts artifacts &&
222221
pip3 install -r requirements/base.txt &&
223-
~/.local/bin/taskgraph action-callback
222+
taskgraph action-callback
224223
else: >
225224
cd /builds/worker/checkouts/src &&
226225
ln -s /builds/worker/artifacts artifacts &&
227-
~/.local/bin/taskgraph decision
226+
taskgraph decision
228227
--pushlog-id='0'
229228
--pushdate='0'
230229
--project='${project}'

Pipfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ requests = "*"
1313
pytest-xdist = "*"
1414
pytest-html = "*"
1515
pypom = "*"
16+
taskcluster-taskgraph = "==9.0.0"
1617
jsonpath-ng = "*"
1718
pillow = "*"
1819
pyfxa = "*"
20+
ruff = "0.5.2"
21+
pytest-rerunfailures = "*"
1922

2023
[dev-packages]
2124
werkzeug = "*"
2225
selenium-wire = "*"
2326
pdoc = "*"
24-
ruff = "0.4.3"
2527
pytest-variables = "*"
26-
taskcluster-taskgraph = "*"

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
## fx-desktop-qa-automation
1+
## Project STARfox
2+
**S**moke **T**est **A**utomation **R**epository for Fire*fox* Desktop
3+
24
A Python Selenium based set of tests for smoke testing of Firefox, including Fx incident smoke testing.
35

46
### Build under test

ci_pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ markers = [
1616
testpaths = [
1717
"tests"
1818
]
19-
addopts = "-vs --ci -m 'not incident and not unstable' --html=artifacts/report.html"
19+
addopts = "-vs --ci --reruns 2 --reruns-delay 1 -m 'not incident and not unstable' --html=artifacts/report.html"
2020

2121
[tool.ruff]
2222
target-version = "py310"

collect_executables.sh

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
#!/bin/bash
2+
echo "~~collect executables~~"
23

34
# Usage: ./collect_executables.sh [channel]
45
# Collects geckodriver and Fx, default channel is Beta.
56

67
## Determine OS and arch
78
UNAME_A=$(uname -a)
8-
if [ -n "$WSL_DISTRO_NAME" ]
9+
## Save the system arch info
10+
echo "uname -a: ${UNAME_A}"
11+
if [ -n "$WSL_DISTRO_NAME" ] || [[ $UNAME_A == *"MINGW"* ]]
912
then
1013
SYSTEM_NAME="win"
14+
if [[ $UNAME_A == *"x86_64"* ]]
15+
then
16+
BITS="64"
17+
else
18+
BITS="32"
19+
fi
1120
else
1221
if [[ $UNAME_A == *"Darwin"* ]]
1322
then
@@ -29,11 +38,6 @@ else
2938
fi
3039
fi
3140

32-
if [[ "$SYSTEM_NAME" == "win" ]] && [[ -z $ARCH ]] && [[ $BITS = "64" ]]
33-
then
34-
BITS=32
35-
fi
36-
3741
if [[ $SYSTEM_NAME == "win" ]]
3842
then
3943
EXT="zip"
@@ -43,14 +47,13 @@ fi
4347

4448
# Find the version of Geckodriver that matches arch
4549
FILENAME="-${SYSTEM_NAME}${BITS}${ARCH}.${EXT}"
46-
50+
echo "FILENAME ${FILENAME}"
4751
# 20 is arbitrary and may break if future releases of Geckodriver have more than 20 channels
4852
for i in {0..20}
4953
do
5054
GECKO_LINK=$(curl -s https://api.github.com/repos/mozilla/geckodriver/releases/latest | jq ".[\"assets\"][${i}][\"browser_download_url\"]" | tr -d '"')
5155
if [[ $GECKO_LINK == *"${FILENAME}"* ]] && [[ $GECKO_LINK != *".asc" ]]
5256
then
53-
echo "$GECKO_LINK"
5457
curl -OL "$GECKO_LINK"
5558
fi
5659
done
@@ -83,9 +86,26 @@ FX_LOC=$(echo "$FX_LINK_HTML" | awk -F '"' '{print $2}')
8386

8487
curl -O "$FX_LOC"
8588

86-
mv geckodriver*.tar.gz geckodriver.tar.gz
87-
tar -xvzf geckodriver.tar.gz
89+
GD_FILE=$(ls geckodriver*)
90+
mv "$GD_FILE" "geckodriver.${EXT}"
91+
if [[ $EXT == "zip" ]]
92+
then
93+
unzip geckodriver.zip
94+
else
95+
tar -xvzf geckodriver.tar.gz
96+
fi
97+
98+
# Wait up to 10 seconds for geckodriver to exist
99+
for ((i=0; i<200; i++))
100+
do
101+
if [ -f geckodriver ]
102+
then
103+
break
104+
fi
105+
sleep 0.2
106+
done
88107
chmod +x geckodriver
108+
./geckodriver --version
89109

90110
if [[ $SYSTEM_NAME == "linux" ]]
91111
then

conftest.py

Lines changed: 89 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,75 @@
1+
import datetime
12
import logging
23
import os
34
import platform
5+
import re
46
from typing import Callable, List, Tuple
57

68
import pytest
7-
from selenium import webdriver
9+
from selenium.common.exceptions import TimeoutException, WebDriverException
10+
from selenium.webdriver import Firefox
11+
from selenium.webdriver.common.by import By
812
from selenium.webdriver.firefox.options import Options
13+
from selenium.webdriver.support import expected_conditions as EC
14+
from selenium.webdriver.support.ui import WebDriverWait
15+
16+
17+
def screenshot_content(driver: Firefox, opt_ci: bool, test_name: str) -> None:
18+
"""
19+
Screenshots the current browser, saves with appropriate test name and date for reference
20+
"""
21+
current_time = str(datetime.datetime.now())
22+
current_time = re.sub(r"[^\w_. -]", "_", current_time)
23+
filename = f"{test_name}_{current_time}_image"
24+
_screenshot(filename, driver, opt_ci)
25+
26+
27+
def log_content(opt_ci: bool, driver: Firefox, test_name: str) -> None:
28+
"""
29+
Logs the current browser content, with the appropriate test name and date for reference.
30+
"""
31+
artifacts_loc = "artifacts" if opt_ci else ""
32+
current_time = str(datetime.datetime.now())
33+
current_time = re.sub(r"[^\w_. -]", "_", current_time)
34+
fullpath_chrome = os.path.join(
35+
artifacts_loc, f"{test_name}_{current_time}_content.txt"
36+
)
37+
fullpath_content = os.path.join(
38+
artifacts_loc, f"{test_name}_{current_time}_chrome.txt"
39+
)
40+
41+
# Save Chrome context page source
42+
with open(fullpath_chrome, "w", encoding="utf-8") as fh:
43+
with driver.context(driver.CONTEXT_CHROME):
44+
output_contents = driver.page_source
45+
fh.write(output_contents)
46+
47+
# Save Content context page source
48+
with open(fullpath_content, "w", encoding="utf-8") as fh:
49+
output_contents = driver.page_source
50+
fh.write(output_contents)
51+
return
52+
53+
54+
def pytest_exception_interact(node, call, report):
55+
"""
56+
Method that wraps all test execution, on any exception/failure an artifact with the information about the failure is kept.
57+
"""
58+
if report.failed:
59+
try:
60+
test_name = node.name
61+
logging.error(f"Handling exception for test: {test_name}")
62+
logging.error(
63+
f"NODE LOGS HERE {node.funcargs}\n THE FAILED TEST: {test_name}"
64+
)
65+
driver = node.funcargs.get("driver")
66+
opt_ci = node.funcargs.get("opt_ci")
67+
if driver:
68+
log_content(opt_ci, driver, test_name)
69+
screenshot_content(driver, opt_ci, test_name)
70+
except Exception as e:
71+
logging.warning("Something went wrong with the exception catching.")
72+
raise e
973

1074

1175
def pytest_addoption(parser):
@@ -53,6 +117,17 @@ def pytest_addoption(parser):
53117
)
54118

55119

120+
def _screenshot(filename: str, driver: Firefox, opt_ci: bool):
121+
if not filename.endswith(".png"):
122+
filename = filename + ".png"
123+
artifacts_loc = ""
124+
if opt_ci:
125+
artifacts_loc = "artifacts"
126+
fullpath = os.path.join(artifacts_loc, filename)
127+
driver.save_screenshot(fullpath)
128+
return fullpath
129+
130+
56131
@pytest.fixture()
57132
def opt_headless(request):
58133
return request.config.getoption("--run-headless")
@@ -166,7 +241,7 @@ def driver(
166241
options.binary_location = fx_executable
167242
for opt, value in set_prefs:
168243
options.set_preference(opt, value)
169-
driver = webdriver.Firefox(options=options)
244+
driver = Firefox(options=options)
170245
separator = "x"
171246
if separator not in opt_window_size:
172247
if "by" in opt_window_size:
@@ -179,36 +254,31 @@ def driver(
179254
driver.set_window_size(*winsize)
180255
timeout = 30 if opt_ci else opt_implicit_timeout
181256
driver.implicitly_wait(timeout)
257+
WebDriverWait(driver, timeout=40).until(
258+
EC.presence_of_element_located((By.TAG_NAME, "body"))
259+
)
182260
yield driver
183-
261+
except (WebDriverException, TimeoutException) as e:
262+
logging.warning(f"DRIVER exception: {e}")
184263
finally:
185-
driver.quit()
264+
if "driver" in locals() or "driver" in globals():
265+
driver.quit()
186266

187267

188268
@pytest.fixture()
189-
def screenshot(driver: webdriver.Firefox, opt_ci: bool) -> Callable:
269+
def screenshot(driver: Firefox, opt_ci: bool) -> Callable:
190270
"""
191271
Factory fixture that returns a screenshot function.
192272
"""
193273

194-
def _screenshot(filename: str) -> str:
195-
"""
196-
Given a short filename, save a screenshot and return the image's full path.
197-
"""
198-
if not filename.endswith(".png"):
199-
filename = filename + ".png"
200-
artifacts_loc = ""
201-
if opt_ci:
202-
artifacts_loc = "artifacts"
203-
fullpath = os.path.join(artifacts_loc, filename)
204-
driver.save_screenshot(fullpath)
205-
return fullpath
274+
def screenshot_wrapper(filename: str) -> str:
275+
return _screenshot(filename, driver, opt_ci)
206276

207-
return _screenshot
277+
return screenshot_wrapper
208278

209279

210280
@pytest.fixture()
211-
def version(driver: webdriver.Firefox):
281+
def version(driver: Firefox):
212282
return driver.capabilities["browserVersion"]
213283

214284

0 commit comments

Comments
 (0)