chore: bump uPortal 5.17.4 + resource-server 1.5.3 + 2026-05 fleet release wave + post-wave patches#692
Merged
bjagg merged 11 commits intoMay 5, 2026
Conversation
…→ 1.5.3 Problem: uPortal 5.17.3 and resource-server 1.5.3 were both released yesterday. uPortal-start master still pins the prior versions: - uPortalVersion=5.17.2 (released 2026-04-02) - resourceServerWebjarVersion=1.5.0 (released last cycle) The 1.5.0 pin in particular has been actively bad: that release was published without webjar JARs in WEB-INF/lib, so /resource-server/ webjars/jquery/dist/jquery.min.js etc. all 404'd in deployments, which broke the in-browser jQuery 4 / Bootstrap 5 loading. 1.5.3 is the fix. Goal: pull both releases into the fleet's reference Tomcat overlay so adopters consuming uPortal-start get the just-released versions and the webjar serving regression is resolved on master. Changes: - gradle.properties: uPortalVersion 5.17.2 → 5.17.3 - gradle.properties: resourceServerWebjarVersion 1.5.0 → 1.5.3 - resourceServerVersion (the legacy 1.0.48 ResourceServingWebapp pin) intentionally stays as-is — per docs/developer/other/RELEASE.md in the uPortal repo, that one "should generally stay at 1.0.48 in uPortal-start" because the older overlay carries some front-end dependencies the rest of the deploy still needs. Notes: this also unblocks PR uPortal-Project#691 (Playwright BS5 dropdown fixes) — those tests target post-#2915 frontend behavior that only exists in 5.17.3+ and depend on the webjars actually serving (1.5.2+). With this PR landed, uPortal-Project#691 should rebase to a green CI matrix.
ChristianMurphy
approved these changes
Apr 29, 2026
ChristianMurphy
approved these changes
May 4, 2026
…e patches Pull in the 10 Tier 2 portlet wave releases from 2026-05-02/03 plus the 7 post-wave patch releases from 2026-05-04 that fixed regressions caught during uPortal-start smoke testing of the wave. Changes (gradle.properties): - announcementsPortletVersion 2.5.0 -> 2.5.3 (2.5.2 wave + 2.5.3 patch — drop dead Logback ContextDetachingSCL listener) - basicltiPortletVersion 1.5.0 -> 1.5.1 - bookmarksPortletVersion 1.3.0 -> 1.3.1 - calendarPortletVersion 2.7.0 -> 2.7.2 (2.7.1 wave + 2.7.2 patch — fix jackson skew + JSP <% in regex + listener) - feedbackPortletVersion 1.3.0 -> 1.3.1 - jasigWidgetPortletVersion 2.4.0 -> 2.4.2 (2.4.1 wave + 2.4.2 patch — re-add commons-collections + listener) - newsReaderPortletVersion 5.1.0 -> 5.1.4 (5.1.2 wave + 5.1.4 patch; skipped 5.1.3 which had no functional changes) - simpleContentPortletVersion 3.4.0 -> 3.4.2 (3.4.1 wave + 3.4.2 patch — roll portletmvc4spring 5.3.4 -> 5.2.0) - webProxyPortletVersion 2.4.0 -> 2.4.1 Tier 1: - notificationPortletVersion 4.8.0 -> 4.8.3 (4.8.2 wave + 4.8.3 patch — listener) uPortal core: - uPortalVersion 5.17.2 -> 5.17.4 (5.17.3 wave + 5.17.4 patch — listener + Renovate dep bumps) Resource server: - resourceServerWebjarVersion 1.5.0 -> 1.5.3 (legacy ResourceServingWebapp at 1.0.48 stays — separate consolidation track)
ad942fb to
20582b2
Compare
ChristianMurphy
approved these changes
May 4, 2026
Problem: three favorites tests in tests/ux/smoke/favorites.spec.ts asserted on the #up-notification div, which is the legacy noty.js container. uPortal's notification system was modernized: up.notify now maps to ModernNotification.show, which appends a fresh .modern-notification div to <body> rather than populating #up-notification. After the click the legacy div stays empty, so the toBeVisible / toContainText assertions time out. Goal: assert against the element the modern notification system actually renders into, so the favorites tests reflect current behavior. Changes: - swap four #up-notification locators in favorites.spec.ts for .modern-notification (helper plus the three failing tests) Notes: confirmed via a throwaway debug spec that the favorite click populates .modern-notification with the expected text and leaves #up-notification empty.
Problem: bookmarks coverage was render + edit-mode-entry only, so the tests passed even when the underlying CRUD plumbing was broken. The favorites helper that lets you add/remove a bookmark or folder was not exercised end-to-end. Goal: walk the full workflows a real user runs in BookmarksPortlet edit mode, with each test self-cleaning so the suite is repeatable. Changes: - add helpers `emptyBookmarkForm`, `emptyFolderForm`, and entry-by-name locators that scope to the visible "empty" form (multiple identically- shaped forms coexist on the page — error vs. empty variants — so a global selector resolves to several inputs) - add `add, edit, and delete a bookmark` covering: open Add Bookmark form, fill name/url/note, save, click per-entry edit icon, verify pre-population, rename, save, then click delete (auto-accepting the native confirm dialog) and verify removal - add `add and delete a folder` mirroring the same pattern for folders Notes: do not use a renamed value that contains the original as a substring — `hasText` filter does substring match, so an `Initial → Initial (edited)` rename silently passed every assertion. The fix is to use fully distinct names (`Playwright Add ...` vs `Playwright Renamed ...`), which is what landed.
Problem: SimpleContentPortlet coverage stopped at "config mode loads CKEditor for admin" — the actual save path that every web-component- hosting portlet definition (notification-list, customize, favorites- carousel, waffle-menu, etc.) rides on was untested. Goal: assert that an admin can change content via the config form, that the change persists into view mode, and that cancel discards unsaved changes. Keep the spec self-cleaning so repeat runs leave the seed content intact. Changes: - helper `waitForCkeditor` that resolves once CKE has mounted on the textarea, returning the instance id - helper `getCkeditorContent` for capturing the original text - helper `saveContent` that destroys CKEditor first, then writes the new value to the underlying textarea, then submits the form (see Notes below for why) - `admin can save edited content via the CKEditor Save button` — captures original, saves new content, asserts persistence, restores original - `cancel discards unsaved changes` — stages new content, clicks "Return without saving", asserts the marker never persisted Notes: `editor.setData(html, callback)` is asynchronous and the callback can fire before CKEditor's submit-time hook has finished syncing its internal model to the textarea. With the callback form the form posts the *previous* content roughly half the time when the spec is run alongside other admin tests in the suite. Tearing the editor down first (`editor.destroy(true)` — true means update the underlying element on teardown) and then writing the value directly removes every CKEditor surface that could re-sync, so the form posts exactly what we set. Filed upstream as uPortal-Project/SimpleContentPortlet#552 (cms variant has no plain Save button on the form, which is the deeper fragility).
Problem: CalendarPortlet's events column was rendering raw template
source ("<% if (_(days).size() === 0) { %>...<%= day.displayName %>...")
into the visible portlet body instead of interpolated event entries.
The bug is that uPortal's respondr.xsl resets the shared
`up._.templateSettings` to Mustache-style {{ }} delimiters, but
Calendar's templates use the standard <% %> sigils. With the per-call
`_.template(text, settings)` override missing, Underscore returned
the source unchanged.
The fix landed in CalendarPortlet#404. This test stops the bug from
silently coming back the next time someone touches scripts.jsp or
adds a new template.
Goal: assert that the rendered events list never contains literal
Underscore opener sigils. Cheap to keep, fails loudly if the
templateSettings override is dropped from any future template
compile site.
Changes:
- add `event list renders without leaking Underscore template
sigils` to portlets/calendar.spec.ts. Logs in as admin, navigates
to the maximized Calendar, asserts the .upcal-event-list does not
contain the substring `<%` or `<%=`. The list resolves to either
the "No events" alert or one or more day rows — either is fine,
the assertion only cares that no template source leaks through.
Notes: this test fails against the current 2.7.2 release of
CalendarPortlet (where the bug ships) and passes against
2.7.3-SNAPSHOT after the fix is deployed. Pairs with the
uPortalVersion / portlet alignment work on this branch — once the
next CalendarPortlet release lands it will go green on CI.
…Proxy spec
Problem: NewsReader coverage was render-only against a single instance
(campus-news), which missed three things worth catching: that
multiple instances coexist without trampling each other's per-instance
namespace, that offline feeds degrade gracefully instead of dumping
exception text, and that the configured feed labels actually reach
the DOM. Separately, tests/ux/portlets/web-proxy.spec.ts was pointing
at the `snappy` fname under the assumption it was a WebProxyPortlet
instance — `snappy` is actually a SimpleContent portlet, so the spec
was passing trivially against the wrong portlet.
Goal: drive NewsReader coverage to Adequate against the structural
surface that's stable in CI (the upstream RSS feeds aren't stable).
Stop the WebProxy spec from masquerading as coverage when no
WebProxyPortlet instance ships in the quickstart at all.
Changes:
- news-reader.spec.ts: replaced the three render-shape tests with
five targeted ones —
* campus-news renders feed selector with the expected feed names
(Apereo News + Unicon News) so a regression in the
feed-label rendering path fails loudly
* chronicle-wired renders independently as a second instance
(scoped by hasText to avoid the hidden-but-DOM-present sibling
that uPortal's max-mode rendering leaves behind)
* offline feeds degrade gracefully with the "currently unavailable"
message — and explicitly fail if Java exception text leaks
* two instances coexist on the news-fav-collection fragment
without colliding on namespaces
* edit mode loads the feed-management UI (kept from before)
- web-proxy.spec.ts: deleted. The quickstart deploys the WebProxy
WAR but seeds no portlet definition that uses it; even adding one
would need a stable upstream target to exercise URL rewriting,
which is infrastructure we don't have. Coverage doc tracks this
as Out of scope.
Problem: previous web-proxy.spec.ts was already deleted because no WebProxyPortlet instance shipped in the quickstart and the file was silently testing the wrong portlet (`snappy`, which is SimpleContent). Coverage was None / Out of scope as a result, even though the WAR is deployed and ready to use. Goal: stand up a deterministic WebProxy instance against a stable upstream so we can actually exercise the proxy fetch + filter + render path in CI, and lift the portlet's coverage out of "untested". Changes: - data/quickstart/portlet-definition/web-proxy-example.portlet-definition.xml: new catalog-only portlet definition `web-proxy-example`. Targets the IETF-reserved domain (RFC 2606) so the body text doesn't drift over time. Catalog-only — no fragment-layout subscribes to it, so no default tab is changed and per-role smoke specs are unaffected. contentService=httpContentService + filters=urlRewritingFilter is the minimum viable wiring for the HTTP-fetch path. - tests/ux/portlets/web-proxy.spec.ts: three tests — * proxies http://example.com/ and surfaces the upstream body ("Example Domain", plus the ICANN-maintained policy sentence) * upstream fetch failure surfaces a graceful error message — explicitly fails if Java exception text or HTTP-status boilerplate leaks * no critical JavaScript errors on load Notes: plain HTTP is intentional. https://example.com/ fails handshake against Tomcat's bundled JDK trust store (older trust anchor set vs. modern serving cipher), and example.com responds 200 OK over HTTP without redirect, so plain HTTP keeps the test path working without doctoring the JDK trust store. Captured the reasoning in an XML comment on the location preference. The portlet definition is imported into the running portal via `./gradlew dataImport -Ddir=<dir-containing-this-file>` (dataImport expects a directory, not a single file).
Problem: NotificationPortlet coverage was just two structural smoke
tests in web-components.spec.ts (icon attached, dropdown attached).
Neither asserted that the API actually returned data or that the
portlet rendered any specific entries — so the live JJWT
runtime-classpath bug, where /api/v2 was returning 403 and the
components rendered empty, slipped through coverage entirely.
Goal: lock in the real end-to-end happy path (demo notifications
flow API → web component → DOM) plus the negative auth path, so the
JJWT regression cannot recur silently.
Changes:
- new tests/ux/portlets/notifications.spec.ts with three tests —
* notification-list portlet renders demo entries: asserts the
expected demo titles (Room Available, Bike License Ticket Was
Issued, Past Due Book, Math 101) reach the rendered DOM. If
/api/v2 starts 403'ing again, none of these will appear and
this test fails immediately.
* notification-icon web component shows a badge with a count:
confirms the icon mounts as a custom element and renders a
numeric unread badge for an authenticated student.
* API rejects missing or malformed bearer tokens: negative
guard. With no Authorization header → 403; with a junk JWT →
403. This is the security half of the regression guard — the
happy path landing on 200 only matters if the security filter
is still enforcing.
Notes: the JJWT runtime-classpath bug that broke the API is fixed
in uPortal-Project/NotificationPortlet#695. The above tests
verified the fix end-to-end. The expected-titles list is the
intersection of the two demo response files configured via
DemoNotificationService.locations in etc/portal/notification.properties.
Removed an earlier draft `API returns demo notifications` test
that listened on page.on("response") — it was order-dependent in
the full suite (the listener attached after login may or may not
catch the first /api/v2 fetch), and the DOM-level
"renders demo entries" test already covers the same regression
without that race.
…flakes
Problem: three families of flake were intermittently failing the
chrome-level smoke specs. None were portlet-behavior bugs, but each
made the suite unreliable in full-suite context.
1. Synthetic clicks on Bootstrap 5 dropdown-toggles do not fire the
auto-init delegate. Bootstrap.Dropdown.getInstance(toggle) shows
the instance is wired, but Playwright's .click(), HTMLElement
.click(), and dispatched MouseEvents all leave aria-expanded at
"false". A listener somewhere in uPortal's chrome appears to be
swallowing the click before Bootstrap's document-level delegate
can match it (root cause still unidentified — tracked separately).
2. The favorites add/remove API returns 200 (and fires the
"modern-notification" success toast) before uPortal has
invalidated the server-side layout cache. An immediate
page.reload() can render the *previous* favorite state, so
assertions like "after-remove the link should say Add to my
Favorites" fail in suite context where preceding tests added
contention.
3. The Welcome-tab favorites count test hard-coded toHaveCount(3)
against the seeded layout's three portlets, but the student
user's persisted layout accumulates favorites (E! Online,
NY Times, Campus News) across sessions and the count drifts
to 6.
Goal: get the smoke specs to a stable green without changing what
they actually verify.
Changes:
- tests/ux/utils/ux-general-utils.ts: add a shared `openDropdown`
helper that opens a Bootstrap dropdown via
`Bootstrap.Dropdown.getOrCreateInstance(el).show()` — the public
Bootstrap API, which works — and asserts aria-expanded after.
Comment captures why we bypass the click pathway.
- tests/ux/smoke/favorites.spec.ts:
* route every dropdown-open through the shared helper
* add `expectFavoriteLinkText` that wraps reload + reopen + assert
in `expect(...).toPass({ timeout: 10000 })` so the second pass
picks up the layout cache invalidation if the first one fired
too early
* replace the hard-coded `toHaveCount(3)` with a dynamic count of
`.up-portlet-wrapper:has(.portlet-options-menu .dropdown-toggle)`
— the semantic invariant is "every portlet with an Options menu
has a favorite item", not "the seed had three of them"
- tests/ux/smoke/portlet-options.spec.ts: route every
dropdown-toggle.click() through the shared helper.
Notes: verified 3/3 full-suite runs pass at 117/117 after the
changes. The tests no longer exercise the dropdown-click pathway
explicitly — they go through Bootstrap's API. That's a tradeoff:
we gained suite stability and lost click-pathway coverage. The
right place to recover the click-pathway test is at the chrome
level once we know what listener is intercepting clicks.
Problem: this branch has been carrying tests and overlay seeds that depend on three companion fixes published over the last few days (uPortal Bootstrap dedupe, CalendarPortlet template-sigil leak, NotificationPortlet jjwt runtime classpath). Until those landed on Maven Central, CI couldn't go green here even though every spec passed locally against the locally-built SNAPSHOTs. Goal: consume the published GA versions so PR uPortal-Project#692 lights up green and is ready to merge. Changes: - uPortalVersion 5.17.4 → 5.17.5 (picks up uPortal-Project/uPortal#2980 — Bootstrap 5 include de-dup in the respondr skin, fixes silently-broken Options menus / favorites / rate-portlet across the dashboard) - calendarPortletVersion 2.7.2 → 2.7.3 (picks up uPortal-Project/CalendarPortlet#404 — Underscore template settings override per call, fixes the events column rendering raw template source instead of event entries) - notificationPortletVersion 4.8.3 → 4.8.4 (picks up uPortal-Project/NotificationPortlet#695 — pin jjwt-impl + jjwt-jackson at runtime, fixes /api/v2 silently 403'ing and the notification web components rendering empty) Notes: locally redeployed the three webapps from Maven Central artifacts and ran the Playwright suite three times in a row — 117/117 each pass — to confirm the published versions match what we were testing against the SNAPSHOTs.
bjagg
added a commit
to bjagg/uPortal-start
that referenced
this pull request
May 5, 2026
Problem: the lint pipeline (`./gradlew playwrightLint`) was not enforcing much. tsconfig was missing `DOM.Iterable`, ESLint had no globals declared for the browser context inside `page.evaluate()` callbacks, and type-coverage was permissive enough that several `(window as any)` casts were riding along without complaint. The deferred chrome flake fix that landed in master via uPortal-Project#692 did not address these gates because the previous PR predated them. Bringing the suite under a strict gate surfaces real type/style issues that we want to catch in CI. Goal: enable the strict lint+type-coverage gate, fix the issues it surfaces, and add the only tests/code change that wasn't already in master from the original uPortal-Project#691 — the `unhidePortletOptionsMenus` helper. Changes — config: - tsconfig.json: add `DOM.Iterable` to `lib` so spread/iteration over NodeList works without casts - eslint.config.js: declare browser globals (window, document, navigator, URL, Element, HTMLElement, NodeListOf, etc.) so no-undef stops flagging legitimate uses inside page.evaluate callbacks; the file is otherwise unchanged - tests/uportal-pw.config.ts: add `trace: "retain-on-failure"` and `screenshot: "only-on-failure"` for CI debug Changes — `unhidePortletOptionsMenus` helper added in `ux-general-utils.ts`: - The XSL renders `.portlet-options-menu` with `class="hidden"` and a jQuery document.ready handler removes the class on portlets that have menu items. In headless Playwright runs the timing is variable enough that the click target may still be hidden when a test interacts with it. The helper force-removes the class as a deterministic prep step. Pairs with the existing `openDropdown` helper — that one drives the Bootstrap-side, this one the upstream jQuery-side that gates whether the toggle is rendered. Changes — under-the-hood typing improvements driven by the new gate: - ux-general-utils.ts: declare `window.bootstrap` global so `openDropdown` doesn't need a triple-cast inside `evaluate` - announcements.spec.ts: declare `window.tinymce` / `window.tinyMCE` globals; use `window.tinymce?.activeEditor?.setContent(...)` instead of `(window as any).tinymce.activeEditor.setContent(...)`; drop one `waitForLoadState("networkidle")` flagged by the playwright plugin; numeric separators on the 10_000 timeouts - simple-content.spec.ts: declare `window.CKEDITOR` global with proper interfaces; use `window.CKEDITOR` directly in evaluate callbacks; replace one `waitForLoadState("networkidle")` with an explicit `waitForURL(/render\.uP/)`; replace nested setData-as-Promise with a fire-and-forget setData (the cancel test only needs *something* staged) - calendar.spec.ts, jasig-widgets.spec.ts, web-proxy.spec.ts: replace `waitForLoadState("networkidle")` with concrete visibility / text assertions on each portlet's distinguishing element; rename `e` → `error` in pageerror handlers - bookmarks.spec.ts: regex case normalization (the `i` flag handles case so the literal can be lowercase) - feedback.spec.ts: `submitBtn` → `submitButton` - favorites.spec.ts: numeric separator on `10_000` - api/utils/api-portlet-list-utils.ts: `.length).not.toEqual(0)` → `.toHaveLength(0)` per the playwright/prefer-to-have-length rule Notes: ran `./gradlew playwrightLint` clean (0 errors, 19 warnings on pre-existing pattern usages that the rules flag as suggestions). Ran the full Playwright suite three times in a row at 117/117 against the released versions to confirm none of the lint-driven refactors changed test behavior. The unhide helper is exported but not yet called from any spec — that's intentional, this PR adds the tool; tests that need it can pull it in as their `openDropdown` calls reveal a hidden-class race in CI.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull all the version pins forward to the 2026-05 fleet release baseline plus the post-wave patches that fixed regressions caught during smoke testing.
Versions bumped
uPortal core:
uPortalVersion5.17.2 → 5.17.4Resource-server (modern, webjar-served):
resourceServerWebjarVersion1.5.0 → 1.5.3Tier 2 portlets — wave + post-wave patches:
Tier 1:
notificationPortletVersion4.8.0 → 4.8.3 (4.8.2 wave + 4.8.3 patch)Smoke-test result
Built locally against released artifacts:
./gradlew portalInit→ BUILD SUCCESSFUL (including NewsReader'sdataInitwhich previously failed withLRUMaperror)./gradlew tomcatStart→ all 18 webapps deploy without errorsContextDetachingSCL/getRenderParameters/LRUMap/ListOrderedMap/JsonSerializeAserrors in the logs (all the wave's regressions confirmed fixed)Out of scope
resourceServerVersion(1.0.48) intentionally stays — legacyResourceServingWebappoverlay is needed by webapps that haven't migrated to webjar-served deps. Tracked separately.esupFilemanagerPortletVersion(4.0.0) — not part of the wave; converted to web component + API in a prior cycle.Test plan
./gradlew portalInitsucceeds end-to-end