Skip to content

Commit f82919c

Browse files
author
Kimmo Virtanen
committed
code cleanup + ci test fix
1 parent ee47e75 commit f82919c

4 files changed

Lines changed: 110 additions & 70 deletions

File tree

polls/static/polls/js/app.js

Lines changed: 48 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
extractPollIdFromSearch,
3131
filterRowsForVisibleDaysAndMinYesVotes,
3232
filterWeekRowsByMinYesVotes,
33+
filterTimezoneSuggestionOptions,
3334
isVoteStatusValue,
3435
loadCalendarTimezonePreferenceValue,
3536
matchesYesVoteFilter,
@@ -48,6 +49,7 @@
4849
|| typeof extractPollIdFromSearch !== "function"
4950
|| typeof filterRowsForVisibleDaysAndMinYesVotes !== "function"
5051
|| typeof filterWeekRowsByMinYesVotes !== "function"
52+
|| typeof filterTimezoneSuggestionOptions !== "function"
5153
|| typeof isVoteStatusValue !== "function"
5254
|| typeof loadCalendarTimezonePreferenceValue !== "function"
5355
|| typeof matchesYesVoteFilter !== "function"
@@ -1512,78 +1514,22 @@ const translations = {
15121514
];
15131515
},
15141516
filteredTimezoneOptions() {
1515-
const options = this.timezoneOptions.map((tz) => {
1516-
const meta = this.timezoneMeta(tz);
1517-
const label = meta ? `${tz} ${meta}` : tz;
1518-
return { id: tz, meta, label };
1519-
});
1520-
const rawQuery = (this.createForm.timezone || "").trim().toLowerCase();
1521-
if (!rawQuery) {
1522-
return options.slice(0, 200);
1523-
}
1524-
return options
1525-
.filter((item) => item.label.toLowerCase().includes(rawQuery))
1526-
.slice(0, 200);
1517+
return this.buildFilteredTimezoneOptions(this.timezoneInputValue("create"));
15271518
},
15281519
filteredEditTimezoneOptions() {
1529-
const options = this.timezoneOptions.map((tz) => {
1530-
const meta = this.timezoneMeta(tz);
1531-
const label = meta ? `${tz} ${meta}` : tz;
1532-
return { id: tz, meta, label };
1533-
});
1534-
const rawQuery = this.editForm && typeof this.editForm.timezone === "string"
1535-
? this.editForm.timezone.trim().toLowerCase()
1536-
: "";
1537-
if (!rawQuery) {
1538-
return options.slice(0, 200);
1539-
}
1540-
return options
1541-
.filter((item) => item.label.toLowerCase().includes(rawQuery))
1542-
.slice(0, 200);
1520+
return this.buildFilteredTimezoneOptions(this.timezoneInputValue("edit"));
15431521
},
15441522
filteredCalendarTimezoneOptions() {
1545-
const options = this.timezoneOptions.map((tz) => {
1546-
const meta = this.timezoneMeta(tz);
1547-
const label = meta ? `${tz} ${meta}` : tz;
1548-
return { id: tz, meta, label };
1549-
});
1550-
const rawQuery = (this.calendarCustomTimezone || "").trim().toLowerCase();
1551-
if (!rawQuery) {
1552-
return options.slice(0, 200);
1553-
}
1554-
return options
1555-
.filter((item) => item.label.toLowerCase().includes(rawQuery))
1556-
.slice(0, 200);
1523+
return this.buildFilteredTimezoneOptions(this.timezoneInputValue("calendar"));
15571524
},
15581525
activeCreateTimezoneSuggestionId() {
1559-
if (
1560-
!this.showTimezoneSuggestions
1561-
|| this.activeTimezoneSuggestionIndex < 0
1562-
|| this.activeTimezoneSuggestionIndex >= this.filteredTimezoneOptions.length
1563-
) {
1564-
return "";
1565-
}
1566-
return this.timezoneSuggestionOptionId("create", this.activeTimezoneSuggestionIndex);
1526+
return this.activeTimezoneSuggestionIdForScope("create");
15671527
},
15681528
activeEditTimezoneSuggestionId() {
1569-
if (
1570-
!this.showEditTimezoneSuggestions
1571-
|| this.activeEditTimezoneSuggestionIndex < 0
1572-
|| this.activeEditTimezoneSuggestionIndex >= this.filteredEditTimezoneOptions.length
1573-
) {
1574-
return "";
1575-
}
1576-
return this.timezoneSuggestionOptionId("edit", this.activeEditTimezoneSuggestionIndex);
1529+
return this.activeTimezoneSuggestionIdForScope("edit");
15771530
},
15781531
activeCalendarTimezoneSuggestionId() {
1579-
if (
1580-
!this.showCalendarTimezoneSuggestions
1581-
|| this.activeCalendarTimezoneSuggestionIndex < 0
1582-
|| this.activeCalendarTimezoneSuggestionIndex >= this.filteredCalendarTimezoneOptions.length
1583-
) {
1584-
return "";
1585-
}
1586-
return this.timezoneSuggestionOptionId("calendar", this.activeCalendarTimezoneSuggestionIndex);
1532+
return this.activeTimezoneSuggestionIdForScope("calendar");
15871533
},
15881534
selectedTimezoneDisplay() {
15891535
return this.timezoneDisplay(this.createForm.timezone);
@@ -2598,6 +2544,45 @@ const translations = {
25982544
}
25992545
return this.filteredTimezoneOptions;
26002546
},
2547+
timezoneInputValue(scope = "create") {
2548+
if (scope === "calendar") {
2549+
return this.calendarCustomTimezone;
2550+
}
2551+
if (scope === "edit") {
2552+
return this.editForm && typeof this.editForm.timezone === "string"
2553+
? this.editForm.timezone
2554+
: "";
2555+
}
2556+
return this.createForm.timezone;
2557+
},
2558+
timezoneSuggestionsOpen(scope = "create") {
2559+
if (scope === "calendar") {
2560+
return this.showCalendarTimezoneSuggestions;
2561+
}
2562+
if (scope === "edit") {
2563+
return this.showEditTimezoneSuggestions;
2564+
}
2565+
return this.showTimezoneSuggestions;
2566+
},
2567+
buildFilteredTimezoneOptions(query = "") {
2568+
return filterTimezoneSuggestionOptions(
2569+
this.timezoneOptions,
2570+
query,
2571+
(timeZone) => this.timezoneMeta(timeZone)
2572+
);
2573+
},
2574+
activeTimezoneSuggestionIdForScope(scope = "create") {
2575+
const options = this.timezoneSuggestionOptions(scope);
2576+
const activeIndex = this.timezoneSuggestionIndex(scope);
2577+
if (
2578+
!this.timezoneSuggestionsOpen(scope)
2579+
|| activeIndex < 0
2580+
|| activeIndex >= options.length
2581+
) {
2582+
return "";
2583+
}
2584+
return this.timezoneSuggestionOptionId(scope, activeIndex);
2585+
},
26012586
timezoneSuggestionIndex(scope = "create") {
26022587
if (scope === "calendar") {
26032588
return this.activeCalendarTimezoneSuggestionIndex;
@@ -2634,12 +2619,7 @@ const translations = {
26342619
this.setTimezoneSuggestionIndex(scope, -1);
26352620
return;
26362621
}
2637-
const rawValue = scope === "calendar"
2638-
? this.calendarCustomTimezone
2639-
: scope === "edit"
2640-
? (this.editForm ? this.editForm.timezone : "")
2641-
: this.createForm.timezone;
2642-
const normalizedValue = String(rawValue || "").trim().toLowerCase();
2622+
const normalizedValue = String(this.timezoneInputValue(scope) || "").trim().toLowerCase();
26432623
const matchedIndex = options.findIndex((item) => item.id.toLowerCase() === normalizedValue);
26442624
this.setTimezoneSuggestionIndex(scope, matchedIndex >= 0 ? matchedIndex : 0);
26452625
},

polls/static/polls/js/app_logic.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,34 @@
175175
).sort((left, right) => left - right);
176176
}
177177

178+
function filterTimezoneSuggestionOptions(timezoneOptions, query, buildMeta, limit = 200) {
179+
const metaBuilder = typeof buildMeta === "function" ? buildMeta : () => "";
180+
const normalizedLimit = Number.isInteger(limit) && limit > 0 ? limit : 200;
181+
const normalizedQuery = String(query || "").trim().toLowerCase();
182+
const options = Array.isArray(timezoneOptions)
183+
? timezoneOptions
184+
.map((value) => String(value || "").trim())
185+
.filter(Boolean)
186+
.map((timeZone) => {
187+
const meta = String(metaBuilder(timeZone) || "").trim();
188+
const label = meta ? `${timeZone} ${meta}` : timeZone;
189+
return {
190+
id: timeZone,
191+
meta,
192+
label
193+
};
194+
})
195+
: [];
196+
197+
if (!normalizedQuery) {
198+
return options.slice(0, normalizedLimit);
199+
}
200+
201+
return options
202+
.filter((item) => item.label.toLowerCase().includes(normalizedQuery))
203+
.slice(0, normalizedLimit);
204+
}
205+
178206
function autoGrowScheduleForm(form, votedBounds) {
179207
const nextForm = form && typeof form === "object" ? { ...form } : {};
180208
const normalizedBounds = votedBounds && typeof votedBounds === "object" ? votedBounds : {};
@@ -311,6 +339,7 @@
311339
extractPollIdFromSearch,
312340
filterRowsForVisibleDaysAndMinYesVotes,
313341
filterWeekRowsByMinYesVotes,
342+
filterTimezoneSuggestionOptions,
314343
isVoteStatusValue,
315344
loadCalendarTimezonePreferenceValue,
316345
matchesYesVoteFilter,

polls/static/polls/js/tests/unit_tests.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ function registerUnitTests(harness) {
88
extractPollIdFromSearch,
99
filterRowsForVisibleDaysAndMinYesVotes,
1010
filterWeekRowsByMinYesVotes,
11+
filterTimezoneSuggestionOptions,
1112
isVoteStatusValue,
1213
loadCalendarTimezonePreferenceValue,
1314
matchesYesVoteFilter,
@@ -116,6 +117,22 @@ function registerUnitTests(harness) {
116117
);
117118
});
118119

120+
test("filterTimezoneSuggestionOptions filters using timezone id and meta text", () => {
121+
const results = filterTimezoneSuggestionOptions(
122+
["UTC", "Europe/Helsinki", "Europe/Stockholm"],
123+
"helsinki utc+3",
124+
(timeZone) => (timeZone === "Europe/Helsinki" ? "UTC+3" : "")
125+
);
126+
127+
assertDeepEqual(results, [
128+
{
129+
id: "Europe/Helsinki",
130+
meta: "UTC+3",
131+
label: "Europe/Helsinki UTC+3"
132+
}
133+
]);
134+
});
135+
119136
test("collectDayOptionIdsFromRows returns ids for one visible day", () => {
120137
const rows = [
121138
{ cells: { "2026-04-01": { id: 11 }, "2026-04-02": { id: 12 } } },

polls/tests_browser.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616

1717
sync_playwright_fn: Any = None
1818
axe_runner_factory: Any = None
19+
playwright_timeout_error_cls: Any = TimeoutError
1920

2021
try:
2122
from axe_playwright_python.sync_playwright import Axe as imported_axe_runner
23+
from playwright.sync_api import TimeoutError as imported_playwright_timeout_error
2224
from playwright.sync_api import sync_playwright as imported_sync_playwright
2325

2426
sync_playwright_fn = imported_sync_playwright
2527
axe_runner_factory = imported_axe_runner
28+
playwright_timeout_error_cls = imported_playwright_timeout_error
2629
PLAYWRIGHT_TESTS_AVAILABLE = True
2730
except ImportError:
2831
PLAYWRIGHT_TESTS_AVAILABLE = False
@@ -161,8 +164,19 @@ def run_accessibility_audit(
161164

162165
def open_home_page(self, page: Optional["Page"] = None, path: str = "/") -> None:
163166
page = page or self.require_page()
164-
page.goto(path, wait_until="domcontentloaded", timeout=30000)
165-
page.wait_for_load_state("networkidle", timeout=30000)
167+
for attempt in range(2):
168+
try:
169+
page.goto(path, wait_until="domcontentloaded", timeout=30000)
170+
page.wait_for_load_state("networkidle", timeout=30000)
171+
break
172+
except playwright_timeout_error_cls:
173+
if attempt == 1:
174+
raise
175+
try:
176+
page.goto("about:blank", wait_until="load", timeout=5000)
177+
except playwright_timeout_error_cls:
178+
pass
179+
page.wait_for_timeout(250)
166180
page.locator("#app").wait_for(state="visible")
167181
page.get_by_role("heading", name="TimePoll").wait_for()
168182

0 commit comments

Comments
 (0)