Skip to content

Commit 909c290

Browse files
authored
Merge branch 'main' into ben/new-manifests
2 parents 0becacd + fb70cee commit 909c290

21 files changed

+506
-33
lines changed

.github/workflows/l10n.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ jobs:
195195
echo $gecko_url
196196
Invoke-WebRequest -Uri $gecko_url -OutFile "geckodriver.zip"
197197
unzip geckodriver.zip
198+
echo "$env:GITHUB_WORKSPACE" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
198199
shell: pwsh
199200
- name: Download Beta
200201
if: ${{ !inputs.win_installer_link }}
@@ -228,7 +229,7 @@ jobs:
228229
$line = $_
229230
Write-Host "Running tests for: $line"
230231
try {
231-
pipenv run python -m l10n_CM.run_l10n --geckodriver=geckodriver.exe --fx-executable="$env:FX_EXECUTABLE" $line
232+
pipenv run python -m l10n_CM.run_l10n --fx-executable="$env:FX_EXECUTABLE" $line
232233
} catch {
233234
$SCRIPT_EXIT_CODE = $_.Exception.HResult
234235
}

SELECTOR_INFO.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2330,6 +2330,13 @@ Description: bsky website sign in button
23302330
Location: Bsky website
23312331
Path to .json: modules/data/generic_page.components.json
23322332
```
2333+
```
2334+
Selector Name: url-to-copy
2335+
Selector Data: "p[contenteditable]"
2336+
Description: Clipboard page to copy url
2337+
Location: Clipboard test page
2338+
Path to .json: modules/data/generic_page.components.json
2339+
```
23332340
#### generic_pdf
23342341
```
23352342
Selector Name: highlighted-text
@@ -3273,7 +3280,27 @@ Description: Searchbar search engine button
32733280
Location: Searchbar
32743281
Path to .json: modules/data/navigation.components.json
32753282
```
3276-
searchbar-search-engine
3283+
```
3284+
Selector Name: search-mode-chicklet
3285+
Selector Data: label.searchmode-switcher-title
3286+
Description: Search mode chicklet
3287+
Location: Address bar search mode
3288+
Path to .json: modules/data/navigation.components.json
3289+
```
3290+
```
3291+
Selector Name: history-button-search-mode
3292+
Selector Data: search-button-history
3293+
Description: Histroy button search mode
3294+
Location: Search mode
3295+
Path to .json: modules/data/navigation.components.json
3296+
```
3297+
```
3298+
Selector Name: exit-button-searchmode
3299+
Selector Data: toolbarbutton[data-l10n-id='urlbar-searchmode-exit-button']
3300+
Description: Exit button searchmode
3301+
Location: Address bar searchmode
3302+
Path to .json: modules/data/navigation.components.json
3303+
```
32773304
#### panel_ui
32783305
```
32793306
Selector name: panel-ui-button

l10n_CM/constants/demo/demo_ad.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"email": "email",
1212
"telephone": "tel"
1313
},
14+
1415
"fields": [
1516
"name",
1617
"organization",

l10n_CM/region/AT.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"libro",
1111
"mediamarkt"
1212
],
13+
1314
"tests": [
1415
]
1516
}

l10n_CM/run_l10n.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class MyHttpRequestHandler(SimpleHTTPRequestHandler):
122122
region = None
123123

124124
def translate_path(self, path):
125-
"""switch the default directory where the html files are served from."""
125+
"""switch the default directory where the HTML files are served from."""
126126
base_dir = os.path.join(current_dir, "sites", self.live_site, self.region)
127127
return os.path.join(base_dir, path.lstrip("/"))
128128

modules/browser_object_navigation.py

Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import re
33
import time
44
from typing import Literal
5+
from urllib.parse import urlparse
56

67
from selenium.common.exceptions import StaleElementReferenceException, TimeoutException
78
from selenium.webdriver import ActionChains, Firefox
@@ -90,15 +91,6 @@ def type_in_awesome_bar(self, term: str) -> BasePage:
9091
self.awesome_bar.send_keys(term)
9192
return self
9293

93-
@BasePage.context_chrome
94-
def press_ctrl_enter(self) -> BasePage:
95-
"""Press Ctrl/Cmd + Enter in Awesome Bar."""
96-
if self.sys_platform() == "Darwin":
97-
self.perform_key_combo(Keys.COMMAND, Keys.ENTER)
98-
else:
99-
self.perform_key_combo(Keys.CONTROL, Keys.ENTER)
100-
return self
101-
10294
def set_search_mode_via_awesome_bar(self, mode: str) -> BasePage:
10395
"""
10496
Given a `mode`, set the Awesome Bar search mode. Returns self.
@@ -382,10 +374,11 @@ def wait_for_suggestions_absent(self):
382374
self.element_not_visible("suggestion-titles")
383375
return self
384376

385-
def open_usb_and_select_engine(self, engine_title: str):
386-
"""Click the USB icon and select a search engine by its title."""
377+
@BasePage.context_chrome
378+
def open_usb_and_select_option(self, option_title: str):
379+
"""Click the USB icon and select an option by its title."""
387380
self.get_element("searchmode-switcher").click()
388-
self.get_element("search-mode-switcher-option", labels=[engine_title]).click()
381+
self.get_element("search-mode-switcher-option", labels=[option_title]).click()
389382
return self
390383

391384
def assert_search_mode_chip_visible(self):
@@ -394,6 +387,40 @@ def assert_search_mode_chip_visible(self):
394387
self.get_element("search-mode-span")
395388
return self
396389

390+
@BasePage.context_chrome
391+
def verify_search_mode_is_visible(self):
392+
"""Ensure the search mode is visible in URLbar"""
393+
self.element_visible("search-mode-chicklet")
394+
return self
395+
396+
@BasePage.context_chrome
397+
def verify_search_mode_is_not_visible(self):
398+
"""Ensure the search mode is cleared from URLbar"""
399+
self.element_not_visible("search-mode-chicklet")
400+
return self
401+
402+
@BasePage.context_chrome
403+
def verify_search_mode_label(self, engine_name: str):
404+
"""Verify that the search mode chicklet displays the correct engine."""
405+
chicklet = self.get_element("search-mode-chicklet")
406+
chip_text = (
407+
chicklet.text or chicklet.get_attribute("aria-label") or ""
408+
).lower()
409+
assert engine_name.lower() in chip_text, (
410+
f"Expected search mode engine '{engine_name}', got '{chip_text}'"
411+
)
412+
return self
413+
414+
@BasePage.context_chrome
415+
def verify_plain_text_in_input_awesome_bar(self, expected_text: str):
416+
"""Verify the awesomebar input contains the exact literal text."""
417+
input_el = self.get_element("awesome-bar")
418+
value = input_el.get_attribute("value")
419+
assert value == expected_text, (
420+
f"Expected input '{expected_text}', got '{value}'"
421+
)
422+
return self
423+
397424
def click_first_suggestion_row(self):
398425
"""
399426
Clicks the first visible suggestion row in the list, using robust scrolling and fallback.
@@ -907,6 +934,27 @@ def verify_status_panel_url(self, expected_url: str):
907934
f"Expected '{expected_url}' in status panel URL, got '{actual_url}'"
908935
)
909936

937+
@BasePage.context_content
938+
def verify_domain(self, expected_domain: str) -> None:
939+
"""
940+
Verify that the current URL's domain matches the expected domain using urlparse.
941+
This explicitly checks the domain (netloc) rather than just a substring match.
942+
Uses content context to get the actual page URL.
943+
944+
Argument:
945+
expected_domain: The expected domain (e.g., "wikipedia.org", "google.com")
946+
"""
947+
948+
def _domain_matches(_):
949+
parsed = urlparse(self.driver.current_url)
950+
return expected_domain in parsed.netloc
951+
952+
self.custom_wait(timeout=15).until(_domain_matches)
953+
parsed_url = urlparse(self.driver.current_url)
954+
assert expected_domain in parsed_url.netloc, (
955+
f"Expected '{expected_domain}' domain, got '{parsed_url.netloc}'"
956+
)
957+
910958
@BasePage.context_chrome
911959
def verify_engine_returned(self, engine: str) -> None:
912960
"""
@@ -996,3 +1044,15 @@ def add_search_bar_to_toolbar(self) -> BasePage:
9961044
self.panel_ui.navigate_to_customize_toolbar()
9971045
self.customize.add_widget_to_toolbar("search-bar")
9981046
return self
1047+
1048+
@BasePage.context_chrome
1049+
def click_exit_button_searchmode(self) -> None:
1050+
"""
1051+
Click the 'Exit' button in the search mode.
1052+
Waits until the button is visible and clickable before performing the click.
1053+
"""
1054+
# Wait until the element is visible and clickable
1055+
self.expect(lambda _: self.get_element("exit-button-searchmode").is_displayed())
1056+
1057+
# Click the button
1058+
self.get_element("exit-button-searchmode").click()

modules/data/generic_page.components.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,11 @@
7676
"selectorData": "//button[@aria-label='Sign in']",
7777
"strategy": "xpath",
7878
"groups": []
79+
},
80+
81+
"url-to-copy": {
82+
"selectorData": "p[contenteditable]",
83+
"strategy": "css",
84+
"groups": []
7985
}
8086
}

modules/data/navigation.components.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,24 @@
645645
"selectorData": "button.searchbar-engine-one-off-item[tooltiptext='{engine}']",
646646
"strategy": "css",
647647
"groups": ["doNotCache"]
648+
},
649+
650+
"search-mode-chicklet": {
651+
"selectorData": "label.searchmode-switcher-title",
652+
"strategy": "css",
653+
"groups": []
654+
},
655+
656+
"history-button-search-mode": {
657+
"selectorData": "search-button-history",
658+
"strategy": "id",
659+
"groups": []
660+
},
661+
662+
"exit-button-searchmode": {
663+
"selectorData": "toolbarbutton[data-l10n-id='urlbar-searchmode-exit-button']",
664+
"strategy": "css",
665+
"groups": []
648666
}
649667

650668
}

tests/address_bar_and_search/test_addon_suggestion.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@
66

77
from modules.browser_object import Navigation
88

9-
WAIT_TIMEOUT = 10
109
ADDONS_BASE_URL = "https://addons.mozilla.org/en-US/firefox/addon/"
1110

11+
# pending answers from search team about FX changes here, only grammar works as before
1212
INPUT_TO_ADDON_NAME = {
13-
"clips": "video-downloadhelper",
13+
"video download": "video-downloadhelper",
1414
"grammar": "languagetool",
15-
"Temp mail": "private-relay",
16-
"pics search": "search_by_image",
15+
"alias": "private-relay",
16+
"image finder": "search_by_image",
1717
"darker theme": "darkreader",
18-
"privacy": "privacy-badger17",
19-
"read aloud": "read-aloud",
18+
"accessibility reade": "read-aloud",
2019
}
2120

2221

@@ -28,7 +27,7 @@ def test_case():
2827
@pytest.mark.noxvfb
2928
def test_addon_suggestion_based_on_search_input(driver: Firefox):
3029
"""
31-
C2234714 - Verify that the address bar suggests relevant add-ons based on search input.
30+
C3029292 - Verify that the address bar suggests relevant add-ons based on search input.
3231
"""
3332
nav = Navigation(driver)
3433
nav.set_awesome_bar()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import pytest
2+
from selenium.webdriver import Firefox
3+
from selenium.webdriver.common.keys import Keys
4+
5+
from modules.browser_object import Navigation
6+
7+
8+
@pytest.fixture()
9+
def test_case():
10+
return "3028887"
11+
12+
13+
TAB_1_INPUT = "example"
14+
TAB_1_EXPECTED_DOMAIN = "example.com"
15+
16+
TAB_2_INPUT = "facebook"
17+
TAB_2_EXPECTED_DOMAIN = "facebook.com"
18+
19+
20+
def test_ctrl_enter_refreshes_tab(driver: Firefox):
21+
"""
22+
C3028887 - Verify that Ctrl/Cmd + R refreshes the webpage in the first tab
23+
after navigating with Ctrl/Cmd + Enter.
24+
"""
25+
# Instantiate objects
26+
nav = Navigation(driver)
27+
28+
# Open first tab and navigate using Ctrl+Enter
29+
nav.type_in_awesome_bar(TAB_1_INPUT)
30+
nav.perform_key_combo_chrome(Keys.CONTROL, Keys.ENTER)
31+
nav.url_contains(TAB_1_EXPECTED_DOMAIN)
32+
33+
first_tab_url = driver.current_url
34+
35+
# Open second tab and navigate using Ctrl+Enter
36+
nav.open_and_switch_to_new_window("tab")
37+
nav.type_in_awesome_bar(TAB_2_INPUT)
38+
nav.perform_key_combo_chrome(Keys.CONTROL, Keys.ENTER)
39+
nav.url_contains(TAB_2_EXPECTED_DOMAIN)
40+
41+
# Switch back to first tab
42+
driver.switch_to.window(driver.window_handles[0])
43+
44+
# Refresh using Ctrl/Cmd + R
45+
nav.refresh_page()
46+
47+
# Verify successful reload (same URL)
48+
nav.url_contains(TAB_1_EXPECTED_DOMAIN)
49+
50+
assert driver.current_url == first_tab_url

0 commit comments

Comments
 (0)