Skip to content

e2e: unbreak the Workbench lane (Databricks consent screen + getMetadata driver fallback) - #15209

Open
jonvanausdeln wants to merge 3 commits into
mainfrom
jonv/workbench-databricks-test-493f2c
Open

e2e: unbreak the Workbench lane (Databricks consent screen + getMetadata driver fallback)#15209
jonvanausdeln wants to merge 3 commits into
mainfrom
jonv/workbench-databricks-test-493f2c

Conversation

@jonvanausdeln

@jonvanausdeln jonvanausdeln commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Two independent fixes, both needed to get the Workbench e2e lane green again.

1. Databricks added a consent screen to its OAuth flow

Databricks now ends its OAuth flow on an "Authorize as.." screen at
/oauth-prompt/select-group. The managed-credentials test never got past it, so the
workbench-databricks shard has been failing nightly (e.g.
positron-builds run 29989485459)
with an assertion that points nowhere near the real problem:

Error: expect(locator).toBeVisible() failed
Locator: locator('[aria-label*="Databricks"][aria-label*="Enabled"]')
Timeout: 30000ms

The direct cause wasn't the missing click, it was the success check. We waited on:

waitForURL(/oauth_redirect_callback|localhost:8787/)

and the consent screen's own URL carries the callback in its query string:

https://<workspace>.cloud.databricks.com/oauth-prompt/select-group
  ?next_url=/oidc/v1/authorize...
  &redirect_uri=http%3A%2F%2Flocalhost%3A8787%2Foauth_redirect_callback

Underscores aren't percent-encoded, so oauth_redirect_callback appears literally and the
substring regex matched while we were still sitting on the consent screen. The trace shows the
wait resolving in 2.2s of its 15s budget, then the tab being closed -- the flow was abandoned
mid-consent while the logs reported an accepted OTP.

In test/e2e/pages/workbench/dashboard.page.ts:

  • Match OAuth completion with a (url: URL) => boolean predicate on url.host, not a substring
    regex over the whole URL. Every step of an OAuth flow carries the callback URL in its query
    string, so substring matching is unsafe by construction.
  • Treat reaching either the consent screen or the callback as OTP-accepted, so the existing retry
    loop stops correctly on both paths.
  • Add confirmDatabricksAuthorizePrompt(): click Continue (a link, not a button; the group
    dropdown already preselects the service account's group), then wait for the callback. No-ops
    when Databricks skips the screen.

Snowflake is unaffected: it verifies via widget-state polling, and no other call site used the regex.

2. #15163 broke every Workbench test that starts a session

With the OAuth fix in place the databricks shard got all the way to session startup and then hit
this, as did the default and snowflake shards (14 failed / 18 flaky on default, which does
no OAuth at all):

page.evaluate: TypeError: Cannot read properties of undefined (reading 'executeCommand')
  at Sessions.getMetadata (test/e2e/pages/sessions.ts:755)
  at Sessions.start (test/e2e/pages/sessions.ts:135)

#15163 rewrote getMetadata() to read via the internal _positron.session.getMetadata command.
Both halves of that path are gated on --enable-smoke-test-driver:

Our Electron and browser harnesses pass that flag themselves. The Workbench lane connects to a
server Posit Workbench launched, so neither exists there.

This isn't new -- saveWebClientLogs has failed the same way in this lane for as long as it has
existed (visible in the 07-23 nightly log as Cannot read properties of undefined (reading 'getLogs')), harmlessly, because nothing on the critical path used the driver. #15163 put it there.

Fix: restore the popup-scraping read as a fallback, selected by a new
driver.isDriverAvailable() capability probe. The command path stays the default everywhere it
works, so the flakiness #15163 removed does not come back for Electron/browser; only the Workbench
lane pays the UI cost. The fallback body is a faithful restoration of the pre-#15163 logic, kept
structurally identical (down to the console.focus() that getSessionCount() does on the way
past) since that is the version proven against this lane.

driver.executeCommand has exactly one caller, so getMetadata is the whole blast radius.

Carried in this PR rather than a separate one: Chris is out this week and this blocks the whole
Workbench lane, so it needs to land with the OAuth fix for either to be verifiable in CI. Reviewers
wanting just one of the two can review by commit -- 4724556a59 is the Databricks consent screen,
0ae8278763 is the getMetadata fallback.

Release Notes

New Features

  • N/A

Bug Fixes

  • N/A

Validation Steps

@:workbench @:jupyter @:sessions @:web @:data-explorer

@:workbench runs all four Workbench shards -- databricks is the one the OAuth fix targets,
and all of them exercise the getMetadata fallback. Green on the previous run: databricks,
default, snowflake and azure all passed, where default had been 14 failed / 18 flaky.

@:jupyter is here to check a second lane. app-jupyter, app-workbench and app-external
all reach Positron through an external server, so none of them get --enable-smoke-test-driver
and none of them have window.driver. Jupyter-tagged tests such as data-explorer-r do start R
sessions through sessions.start, so #15163 should have broken that lane the same way -- no
nightly has run since it landed, so this is the first look. If so, the capability probe fixes
Jupyter for free.

@:sessions @:web @:data-explorer mirrors what #15163 validated with, to confirm the capability
probe hasn't disturbed the command path in the lanes that do have the driver. The Windows tag is
dropped for turnaround time; electron, chromium and the four Workbench shards already passed on
the previous run. (Deliberately not naming that tag literally here -- pr-tags-parse.sh greps the
whole description, so even mentioning it in prose re-enables the lane.)

Note that @:workbench-databricks on its own does not enable the lane -- pr-tags-parse.sh
matches a bare @:workbench and explicitly excludes the longer variants. Each shard then applies
its own hardcoded grep.

Databricks added a consent screen at /oauth-prompt/select-group to the end of
its OAuth flow. The Workbench managed-credentials test never got past it, and
failed 30s later on an unrelated-looking assertion:

  expect(locator('[aria-label*="Databricks"][aria-label*="Enabled"]')) failed

The direct cause was not the missing click but the success check. We waited on

  waitForURL(/oauth_redirect_callback|localhost:8787/)

and the consent screen's own URL carries
redirect_uri=http%3A%2F%2Flocalhost%3A8787%2Foauth_redirect_callback in its
query string. Underscores aren't percent-encoded, so the substring regex matched
while we were still parked on the consent screen. The trace shows the wait
resolving in 2.2s of its 15s budget, then the OAuth tab being closed -- the flow
was abandoned mid-consent while the logs reported an accepted OTP.

Changes:

- Match OAuth completion with a (url: URL) => boolean predicate on host, not a
  substring regex over the whole URL. Every step of an OAuth flow carries the
  callback URL in its query string, so substring matching is unsafe by
  construction.
- Treat reaching either the consent screen or the callback as OTP-accepted, so
  the retry loop stops correctly on both paths.
- Add confirmDatabricksAuthorizePrompt(): click Continue (a link, not a button;
  the group dropdown already preselects the service account's group), then wait
  for the callback. No-ops when Databricks skips the screen.

Snowflake is unaffected -- it verifies via widget-state polling, and no other
call site used the regex.
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

E2E Tests 🚀
This PR will run tests tagged with: @:critical @:workbench @:jupyter @:sessions @:web @:data-explorer @:workbench-databricks

Why these tags?
Tag Source
@:critical Always runs (required)
@:workbench PR description
@:jupyter PR description
@:sessions PR description
@:web PR description
@:data-explorer PR description
@:workbench-databricks PR description

More on automatic tags from changed files.

readme  valid tags

#15163 rewrote sessions.getMetadata() to read session metadata through the
internal `_positron.session.getMetadata` command instead of scraping the console
info popup. Both halves of that path are gated on `--enable-smoke-test-driver`:

- `window.driver`, the bridge executeCommand travels over (browser/window.ts)
- the command itself, via registerCommandIfSmokeTestDriver (smokeTestCommands.ts)

Our Electron and browser harnesses pass that flag themselves, but the Workbench
lane connects to a server Posit Workbench launched, so neither exists there. Every
Workbench test that starts an R or Python session then died in the fixture with:

  page.evaluate: TypeError: Cannot read properties of undefined
  (reading 'executeCommand')  at Sessions.getMetadata

That `window.driver` is absent in this lane isn't new -- saveWebClientLogs has been
failing the same way for as long as the lane has existed, harmlessly, because
nothing on the critical path used the driver. #15163 put it there.

Restore the popup-scraping read as a fallback, selected by a new
`driver.isDriverAvailable()` capability probe. The command path stays the default
everywhere it works, so the flakiness #15163 removed does not come back for
Electron/browser; only the Workbench lane pays the UI cost. The fallback body is a
faithful restoration of the pre-#15163 logic, kept structurally identical (down to
the console.focus() that getSessionCount() does on the way past) since that is the
version proven against this lane.

Note that isAlive() cannot serve as the probe: evaluateHandle('window.driver')
resolves to a handle wrapping undefined rather than throwing.
@jonvanausdeln jonvanausdeln changed the title e2e: handle Databricks "Authorize as.." consent screen in OAuth flow e2e: unbreak the Workbench lane (Databricks consent screen + getMetadata driver fallback) Jul 29, 2026
@jonvanausdeln
jonvanausdeln marked this pull request as ready for review July 29, 2026 20:14
@jonvanausdeln
jonvanausdeln requested a review from midleman July 29, 2026 20:14
* @param sessionId the session ID to get metadata for, otherwise will use the current session
* @returns the metadata of the session
*/
private async getMetadataFromDialog(sessionId?: string): Promise<SessionMetaData> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh no! it's all coming back? is there no way to make the command work on workbench? 😭

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants