Skip to content

Verify checkout rendering from the installed app - #8926

Open
atavism wants to merge 18 commits into
mainfrom
atavism/issue-3723
Open

Verify checkout rendering from the installed app#8926
atavism wants to merge 18 commits into
mainfrom
atavism/issue-3723

Conversation

@atavism

@atavism atavism commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Resolves https://github.com/getlantern/engineering/issues/3723

Install and launch Lantern as a Windows user, verify per-user WebView2 storage, and confirm Stripe and Shepherd checkout pages render without navigation errors. Upload complete diagnostics on every run

Summary by CodeRabbit

  • New Features
    • Added Windows nightly “payment checkout smoke” support with workflow toggle, artifact publishing, and end-to-end bootstrapping via command-line arguments.
    • Added configurable smoke flow entry in the app that selects an eligible plan and routes directly to payment method.
  • Bug Fixes
    • Improved Windows smoke automation element lookup and adjusted checkout document polling/error timing.
    • Enhanced webview smoke logging, document length capture, and main-frame error reporting with safer redaction.
    • Refined accessibility semantics for payment method and subscribe/checkout actions.
  • Tests
    • Added unit tests covering payment checkout smoke argument parsing and validation.

Copilot AI review requested due to automatic review settings July 24, 2026 17:11
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Windows-installed payment checkout smoke flow for Stripe and Shepherd, including CLI configuration, app bootstrap, WebView telemetry, UI automation, artifact collection, and CI workflow controls.

Changes

Payment checkout smoke flow

Layer / File(s) Summary
Smoke configuration and startup contract
lib/core/smoke/payment_checkout_smoke.dart, lib/main.dart, lib/lantern_app.dart, test/core/smoke/*
Adds Windows nightly argument parsing, validation, startup logging, app wiring, and parser tests.
Checkout bootstrap and WebView observability
lib/lantern_app.dart, lib/core/widgets/app_webview.dart, lib/features/auth/choose_payment_method.dart
Loads staging plans, opens payment flow, records WebView lifecycle and error events, redacts URLs, and adds provider-specific semantics.
Installed Windows checkout case runner
.github/scripts/windows_payment_checkout_smoke.ps1
Uses AutomationId-based lookup and revised checkout log polling for installed-app UI automation.
Smoke suite and workflow orchestration
.github/scripts/windows_smoke_suite.ps1, .github/workflows/app-smoke-tests.yml, .github/workflows/build-windows.yml
Adds workflow controls, installer-only execution, smoke invocation, and conditional artifact upload.

Radiance dependency update

Layer / File(s) Summary
Radiance module revision
go.mod
Updates the required Radiance pseudo-version.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Workflow
  participant SmokeSuite
  participant PaymentSmoke
  participant LanternApp
  participant WebViewLogs
  Workflow->>SmokeSuite: enable installed payment smoke
  SmokeSuite->>PaymentSmoke: run checkout cases
  PaymentSmoke->>LanternApp: launch app as disposable user
  LanternApp->>WebViewLogs: record checkout lifecycle events
  PaymentSmoke->>WebViewLogs: validate checkout events
  PaymentSmoke-->>Workflow: write and upload artifacts
Loading

Possibly related PRs

Suggested reviewers: copilot, jigar-f

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes unrelated changes, including an iOS comment cleanup and a go.mod dependency bump, beyond the Windows checkout smoke scope. Remove the unrelated cleanup and split or justify the dependency bump in a separate PR unless it's required for the smoke flow.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: verifying checkout rendering from the installed app.
Linked Issues check ✅ Passed The PR implements the first linked layer: installed-app checkout rendering checks that stop before payment.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch atavism/issue-3723

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

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.

Pull request overview

Adds an installed-app Windows smoke workflow to validate that WebView2 uses per-user storage and that Stripe/Shepherd checkout pages render successfully (without main-frame navigation errors), with diagnostics captured as artifacts.

Changes:

  • Introduces a PaymentCheckoutSmokeConfig CLI hook and wires it through app startup to auto-navigate into the checkout flow on Windows nightly builds.
  • Adds WebView instrumentation logs for “created / load_start / load_stop / navigation_error” events and document-length probing to detect “blank” checkout renders.
  • Extends GitHub Actions Windows smoke suite with a new “payment checkout smoke” option and a dedicated UIAutomation PowerShell runner that uploads artifacts.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/core/smoke/payment_checkout_smoke_test.dart Unit tests for PaymentCheckoutSmokeConfig.parse() argument validation and derived email behavior.
lib/main.dart Accepts Dart entrypoint arguments, logs WebView2 diagnostics on Windows, parses/propagates checkout smoke config into the app.
lib/lantern_app.dart On smoke enablement, bootstraps plans/provider state and routes directly to ChoosePaymentMethod to start the flow.
lib/features/auth/choose_payment_method.dart Adds Semantics labels to make provider/button discoverable via Windows UIAutomation during smokes.
lib/core/widgets/app_webview.dart Emits structured smoke logs around WebView lifecycle/loading/errors and captures document length.
lib/core/smoke/payment_checkout_smoke.dart New config parser enforcing Windows+nightly constraint, provider allowlist, and UUID run-id validation.
go.mod Updates github.com/getlantern/radiance dependency version.
go.sum Updates checksums for the new radiance versions.
.github/workflows/build-windows.yml Adds input + artifact upload for Windows payment checkout smoke artifacts.
.github/workflows/app-smoke-tests.yml Plumbs new Windows payment checkout smoke toggle into the Windows build/smoke workflow call.
.github/scripts/windows_smoke_suite.ps1 Adds orchestration switch and invocation of the new payment checkout smoke script.
.github/scripts/windows_payment_checkout_smoke.ps1 New end-to-end installed-app UIAutomation script: disposable user, staging service, provider selection, WebView log assertions, and artifact capture.
Comments suppressed due to low confidence (1)

lib/features/auth/choose_payment_method.dart:606

  • The checkout PrimaryButton is wrapped in Semantics(excludeSemantics: true, label: 'payment-checkout-...'), which causes assistive technologies to announce the technical automation label rather than the visible button label ('Subscribe'/'Checkout'). This is an accessibility regression. Prefer preserving the button’s normal semantics and exposing a separate stable automation identifier that doesn’t override the spoken label (and only enable it during the smoke run if needed).
              Semantics(
                container: true,
                excludeSemantics: true,
                label: 'payment-checkout-${method.providers.name}',
                button: true,
                enabled: !isSubmitting,
                onTap: isSubmitting ? null : () => onSubscribe.call(method),
                child: PrimaryButton(

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +501 to +505
title: Semantics(
container: true,
excludeSemantics: true,
label: 'payment-provider-${method.providers.name}',
child: Row(

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
.github/scripts/windows_payment_checkout_smoke.ps1 (1)

271-279: 🩺 Stability & Availability | 🔵 Trivial | ⚖️ Poor tradeoff

Transient/intermediate WebView errors are treated as globally fatal.

This scans the entire accumulated log each poll and throws on any navigation_error / document_error anywhere. During redirect-heavy provider flows, app_webview.dart emits navigation_error for any main-frame error (incl. ERR_ABORTED on redirect) and document_error when evaluateJavascript fails on an intermediate load — even when the final checkout page renders fine. This can cause false CI failures. Consider only failing when no successful load_stop for the expected host was captured, or scoping the fatal checks to the target host/final navigation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/windows_payment_checkout_smoke.ps1 around lines 271 - 279,
Update the polling logic around the accumulated $logText checks so transient
navigation_error and document_error events do not immediately fail
redirect-heavy flows. Track or detect a successful load_stop for the expected
checkout host, and only throw the existing WebView errors when that final target
navigation was not successfully reached; keep the checkout
rejected/bootstrap_error failure handling unchanged.
lib/features/auth/choose_payment_method.dart (1)

501-515: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Automation hooks override the human-readable a11y labels. Both sites use excludeSemantics: true with a machine label, which drops descendant semantics so screen readers announce payment-provider-<name> / payment-checkout-<name> instead of the localized provider name and the Subscribe/Checkout button label — an accessibility regression introduced purely to give UI Automation a NameProperty to match. Prefer SemanticsProperties.identifier (exposed as the UIA AutomationId) for the automation hook and search by AutomationIdProperty in the PowerShell, leaving the human-readable label intact.

  • lib/features/auth/choose_payment_method.dart#L501-L515: replace the label/excludeSemantics override on the provider title with an identifier, keeping the provider Text/logo semantics.
  • lib/features/auth/choose_payment_method.dart#L599-L615: same for the checkout button so the localized Subscribe/Checkout label is still announced; update windows_payment_checkout_smoke.ps1 Find-AutomationElement to query AutomationIdProperty.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/features/auth/choose_payment_method.dart` around lines 501 - 515, Replace
the provider title Semantics override around
lib/features/auth/choose_payment_method.dart#L501-L515 with identifier set to
the automation hook, removing excludeSemantics and the machine label so the
provider Text and logo remain announced. Apply the same change to the checkout
button at lib/features/auth/choose_payment_method.dart#L599-L615, preserving its
localized Subscribe/Checkout label, and update
windows_payment_checkout_smoke.ps1's Find-AutomationElement to query
AutomationIdProperty instead of NameProperty.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/scripts/windows_payment_checkout_smoke.ps1:
- Around line 271-279: Update the polling logic around the accumulated $logText
checks so transient navigation_error and document_error events do not
immediately fail redirect-heavy flows. Track or detect a successful load_stop
for the expected checkout host, and only throw the existing WebView errors when
that final target navigation was not successfully reached; keep the checkout
rejected/bootstrap_error failure handling unchanged.

In `@lib/features/auth/choose_payment_method.dart`:
- Around line 501-515: Replace the provider title Semantics override around
lib/features/auth/choose_payment_method.dart#L501-L515 with identifier set to
the automation hook, removing excludeSemantics and the machine label so the
provider Text and logo remain announced. Apply the same change to the checkout
button at lib/features/auth/choose_payment_method.dart#L599-L615, preserving its
localized Subscribe/Checkout label, and update
windows_payment_checkout_smoke.ps1's Find-AutomationElement to query
AutomationIdProperty instead of NameProperty.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2072b4ae-cbed-42d2-b247-4fc58d0177c3

📥 Commits

Reviewing files that changed from the base of the PR and between 3a5c4c1 and 2bef279.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • .github/scripts/windows_payment_checkout_smoke.ps1
  • .github/scripts/windows_smoke_suite.ps1
  • .github/workflows/app-smoke-tests.yml
  • .github/workflows/build-windows.yml
  • go.mod
  • lib/core/smoke/payment_checkout_smoke.dart
  • lib/core/widgets/app_webview.dart
  • lib/features/auth/choose_payment_method.dart
  • lib/lantern_app.dart
  • lib/main.dart
  • test/core/smoke/payment_checkout_smoke_test.dart

Copilot AI review requested due to automatic review settings July 29, 2026 14:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
go.mod (1)

28-28: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Fix the current dependency failure before relying on this rollback.

With the selected Radiance revision pinned, go test ./... fails because golang.getoutline.org/sdk/x/smart and github.com/Jigsaw-Code/outline-sdk/x/smart cannot call lookupCNAME, breaking the module graph. Resolve that dependency issue rather than rolling back Radiance without a working build.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@go.mod` at line 28, Resolve the dependency failure introduced by the pinned
Radiance revision in go.mod: ensure the module graph provides compatible
versions of golang.getoutline.org/sdk/x/smart and
github.com/Jigsaw-Code/outline-sdk/x/smart so their lookupCNAME calls compile
and go test ./... succeeds. Keep the Radiance pin only once the selected
dependency versions build successfully.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@go.mod`:
- Line 28: Resolve the dependency failure introduced by the pinned Radiance
revision in go.mod: ensure the module graph provides compatible versions of
golang.getoutline.org/sdk/x/smart and github.com/Jigsaw-Code/outline-sdk/x/smart
so their lookupCNAME calls compile and go test ./... succeeds. Keep the Radiance
pin only once the selected dependency versions build successfully.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fd5fb566-31f6-48fc-b12a-3eb2f48fab16

📥 Commits

Reviewing files that changed from the base of the PR and between 2bef279 and fc249d3.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (1)
  • go.mod

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

lib/core/widgets/app_webview.dart:193

  • PAYMENT_WEBVIEW_SMOKE is emitted unconditionally for every AppWebView navigation. Since this is a smoke-only diagnostic signal, it should be gated (e.g., Windows + nightly) to avoid adding new info-level logging in normal runs and to keep log semantics accurate.
  void _logSmokeEvent(String event, Uri? uri, {String detail = ''}) {
    // Checkout paths and query strings can contain session tokens. The origin
    // is enough to prove which provider loaded without putting them in CI logs.
    final safeUri = uri == null
        ? '<none>'
        : uri.replace(path: '', query: '', fragment: '').toString();
    final suffix = detail.isEmpty ? '' : ' $detail';
    appLogger.info(
      'PAYMENT_WEBVIEW_SMOKE event=$event host=${uri?.host ?? '<none>'} '
      'url=$safeUri$suffix',
    );
  }

lib/core/widgets/app_webview.dart:150

  • This document_error log line is part of the smoke diagnostics, but it currently bypasses _logSmokeEvent, so it won’t benefit from any gating/redaction logic there. Consider routing it through _logSmokeEvent (and avoid logging the full exception string if it could contain sensitive data).
        } catch (error) {
          appLogger.error(
            'PAYMENT_WEBVIEW_SMOKE event=document_error '
            'host=${uri?.host ?? '<none>'} error=$error',
          );
        }

Comment thread lib/main.dart Outdated
Comment on lines +44 to +50
if (PlatformUtils.isWindows) {
appLogger.info(
'WEBVIEW2_DIAGNOSTIC user_data_folder='
'${Platform.environment['WEBVIEW2_USER_DATA_FOLDER'] ?? '<unset>'} '
'local_app_data=${Platform.environment['LOCALAPPDATA'] ?? '<unset>'}',
);
}
Copilot AI review requested due to automatic review settings July 30, 2026 10:25

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

lib/core/widgets/app_webview.dart:170

  • onReceivedError stops the global WebView loading state and calls _handleCompletionUrl() even for subresource failures (isForMainFrame != true). That can hide the loading indicator while the main frame is still loading, and it risks treating a failing resource URL as a completion URL. Limit the stop/completion handling to main-frame errors only.
      onReceivedError: (_, webResourceRequest, error) async {
        appLogger.error("Received error: $error");
        final uri = Uri.tryParse(webResourceRequest.url.toString());
        _logSmokeEvent(
          webResourceRequest.isForMainFrame == true
              ? 'navigation_error'
              : 'resource_error',
          uri,
          detail: 'error_type=${error.type}',
        );
        ref.read(webViewLoadingProvider.notifier).stop();
        await _handleCompletionUrl(uri);
      },

Copilot AI review requested due to automatic review settings July 30, 2026 17:48

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

lib/features/auth/choose_payment_method.dart:505

  • Using excludeSemantics: true here hides the visible provider name/logo semantics and exposes only the technical label (e.g., "payment-provider-stripe") to assistive technologies. That label is useful for UI automation, but it should not replace the user-facing semantics.
            title: Semantics(
              container: true,
              excludeSemantics: true,
              label: 'payment-provider-${method.providers.name}',
              child: Row(

lib/main.dart:50

  • The WEBVIEW2_DIAGNOSTIC log currently prints the WEBVIEW2_USER_DATA_FOLDER environment variable, but the Windows payment checkout smoke script asserts this field equals the effective per-user WebView2 folder (LOCALAPPDATA\Lantern\WebView2 when the env var is unset). With the smoke harness explicitly clearing WEBVIEW2_USER_DATA_FOLDER, this log will be '' and the smoke will fail even when WebView2 storage is correct.
    if (PlatformUtils.isWindows) {
      appLogger.info(
        'WEBVIEW2_DIAGNOSTIC user_data_folder='
        '${Platform.environment['WEBVIEW2_USER_DATA_FOLDER'] ?? '<unset>'} '
        'local_app_data=${Platform.environment['LOCALAPPDATA'] ?? '<unset>'}',
      );
    }

lib/core/widgets/app_webview.dart:145

  • If evaluateJavascript returns a non-numeric value (but doesn't throw), documentLength becomes -1 without emitting PAYMENT_WEBVIEW_SMOKE event=document_error. The Windows smoke harness looks specifically for document_error (or a positive document_length) and may instead time out with a less-informative failure.
          documentLength = rawLength is num
              ? rawLength.toInt()
              : int.tryParse(rawLength.toString()) ?? -1;
        } catch (error) {

lib/features/auth/choose_payment_method.dart:603

  • excludeSemantics: true on the checkout button wrapper similarly replaces the button's accessible label with a technical identifier ("payment-checkout-…"). This makes screen reader output confusing and can hide the actual button text semantics.
              Semantics(
                container: true,
                excludeSemantics: true,
                label: 'payment-checkout-${method.providers.name}',
                button: true,

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
.github/scripts/windows_payment_checkout_smoke.ps1 (3)

162-192: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fail-fast bootstrap detection duplicates the rejection regex from Wait-CheckoutDocument.

The PAYMENT_CHECKOUT_SMOKE event=(rejected|bootstrap_error) pattern is hardcoded here (line 182) and again in Wait-CheckoutDocument (line 286). If the event name changes in one place, detection silently degrades in the other.

♻️ Proposed fix: extract shared pattern
+$script:CheckoutRejectionPattern = 'PAYMENT_CHECKOUT_SMOKE event=(rejected|bootstrap_error)'
+
 function Find-AutomationElement {
   ...
     if ($FailureLogPath -and (Test-Path $FailureLogPath)) {
       $failure = Select-String -Path $FailureLogPath `
-        -Pattern 'PAYMENT_CHECKOUT_SMOKE event=(rejected|bootstrap_error)' |
+        -Pattern $script:CheckoutRejectionPattern |
         Select-Object -Last 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/windows_payment_checkout_smoke.ps1 around lines 162 - 192,
Extract the shared PAYMENT_CHECKOUT_SMOKE rejection/bootstrap-error regex into a
single script-level pattern variable or constant, then update both
Find-AutomationElement and Wait-CheckoutDocument to reuse it instead of
hardcoding the pattern independently.

381-393: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename $profile to avoid shadowing the automatic $PROFILE variable.

Static analysis flags this twice (lines 381, 383): $profile collides with PowerShell's built-in $PROFILE automatic variable (names are case-insensitive). No functional impact is evident today since $PROFILE isn't otherwise used in this script, but it's a known footgun for future edits in this function.

🔧 Proposed rename
-    $profile = $null
+    $userProfile = $null
     for ($i = 0; $i -lt 15; $i++) {
-      $profile = Get-CimInstance Win32_UserProfile -Filter "SID='$sid'" `
+      $userProfile = Get-CimInstance Win32_UserProfile -Filter "SID='$sid'" `
         -ErrorAction SilentlyContinue
-      if ($profile -and -not [string]::IsNullOrWhiteSpace($profile.LocalPath)) {
+      if ($userProfile -and -not [string]::IsNullOrWhiteSpace($userProfile.LocalPath)) {
         break
       }
       Start-Sleep -Seconds 1
     }
-    if (-not $profile -or [string]::IsNullOrWhiteSpace($profile.LocalPath)) {
+    if (-not $userProfile -or [string]::IsNullOrWhiteSpace($userProfile.LocalPath)) {
       throw "Windows did not create a profile for disposable user $username"
     }
-    $profilePath = [Environment]::ExpandEnvironmentVariables($profile.LocalPath)
+    $profilePath = [Environment]::ExpandEnvironmentVariables($userProfile.LocalPath)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/windows_payment_checkout_smoke.ps1 around lines 381 - 393,
Rename the local `$profile` variable used in the profile lookup and validation
flow to a distinct name such as `$userProfile`, updating all references through
the `Get-CimInstance` loop, null/path checks, and `$profilePath` assignment
while preserving the existing behavior.

Source: Linters/SAST tools


541-541: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

$flutterLog/$finalFlutterLog recompute the same hardcoded path twice.

Line 541 introduces $flutterLog inside the try block; the unchanged finally block (line 603) independently recomputes the identical path as $finalFlutterLog. Since try/finally share the function scope, $flutterLog is already visible in finally when execution reaches that point in the try block, but the separate variable stays needed as a safety net for failures that occur before line 541 executes. Consider hoisting a single path computation to the top of Invoke-CheckoutCase (alongside $caseDirectory/$transcript) so both blocks reference the same variable without relying on execution order.

Also applies to: 603-608

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/windows_payment_checkout_smoke.ps1 at line 541, Hoist the
Flutter log path computation to the start of Invoke-CheckoutCase alongside
$caseDirectory and $transcript, then reuse $flutterLog in both the try block and
finally block. Remove the duplicate $finalFlutterLog computation while
preserving cleanup when execution fails before the try-block assignment.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/scripts/windows_payment_checkout_smoke.ps1:
- Around line 162-192: Extract the shared PAYMENT_CHECKOUT_SMOKE
rejection/bootstrap-error regex into a single script-level pattern variable or
constant, then update both Find-AutomationElement and Wait-CheckoutDocument to
reuse it instead of hardcoding the pattern independently.
- Around line 381-393: Rename the local `$profile` variable used in the profile
lookup and validation flow to a distinct name such as `$userProfile`, updating
all references through the `Get-CimInstance` loop, null/path checks, and
`$profilePath` assignment while preserving the existing behavior.
- Line 541: Hoist the Flutter log path computation to the start of
Invoke-CheckoutCase alongside $caseDirectory and $transcript, then reuse
$flutterLog in both the try block and finally block. Remove the duplicate
$finalFlutterLog computation while preserving cleanup when execution fails
before the try-block assignment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 472cd754-6b2a-499d-8008-e10ab5d52b2e

📥 Commits

Reviewing files that changed from the base of the PR and between fc249d3 and 88a9b4c.

📒 Files selected for processing (2)
  • .github/scripts/windows_payment_checkout_smoke.ps1
  • lib/lantern_app.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/lantern_app.dart

Copilot AI review requested due to automatic review settings July 30, 2026 18:00

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

lib/core/widgets/app_webview.dart:160

  • evaluateJavascript to compute documentLength runs on every onLoadStop across all platforms/build types, even though the result is only used for Windows nightly smoke logging. This adds unnecessary per-navigation overhead and can introduce extra WebView failures/noise outside the smoke scenario. Gate the JS evaluation behind the same Windows-nightly condition used by _logSmokeEvent.
        var documentLength = -1;
        try {
          final rawLength = await controller.evaluateJavascript(
            source:
                "(document.documentElement && document.documentElement.outerHTML || '').length",

.github/scripts/windows_payment_checkout_smoke.ps1:292

  • Wait-CheckoutDocument only checks that some PAYMENT_WEBVIEW_SMOKE event=created appears in the log, not that the created event corresponds to the same host that produced the successful load_stop. That can let the smoke pass even if the checkout WebView missed the creation marker (e.g., if another WebView was created earlier). Scope the check to the matching host.
        if ($hostName -match $HostPattern -and $documentLength -gt 0) {
          if ($lastLogText -notmatch 'PAYMENT_WEBVIEW_SMOKE event=created') {
            throw "The checkout page loaded without a WebView creation marker"
          }

lib/core/smoke/payment_checkout_smoke.dart:54

  • _singleArgument builds a lazy where(...) iterable and then reads length, isEmpty, and single, which can iterate the underlying list multiple times. Converting to a list once keeps the logic straightforward and avoids repeated scans.
    final matches = arguments.where((argument) => argument.startsWith(prefix));

Copilot AI review requested due to automatic review settings July 30, 2026 18:08

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

.github/scripts/windows_payment_checkout_smoke.ps1:309

  • Wait-CheckoutDocument can return success even if the WebView already logged PAYMENT_WEBVIEW_SMOKE event=navigation_error or event=document_error (e.g., main-frame HTTP errors may still produce a load_stop with non-empty HTML). This can mask real checkout failures; treat these error markers as fatal as soon as they appear, before accepting a load_stop match.
      if ($lastLogText -match 'PAYMENT_CHECKOUT_SMOKE event=(rejected|bootstrap_error)') {
        throw "Lantern could not prepare the requested checkout smoke"
      }
      foreach ($match in [regex]::Matches($lastLogText, $linePattern)) {
        $hostName = $match.Groups[1].Value

lib/core/widgets/app_webview.dart:192

  • onReceivedHttpError logs a navigation_error smoke event but does not stop the loading state or run the same completion handling used for other main-frame failures (onReceivedError). If a main-frame HTTP error prevents onLoadStop from firing, the loading indicator can remain stuck and the smoke harness may miss the failure.
      onReceivedHttpError: (_, webResourceRequest, errorResponse) async {
        if (webResourceRequest.isForMainFrame != true) return;
        _logSmokeEvent(
          'navigation_error',
          Uri.tryParse(webResourceRequest.url.toString()),

Copilot AI review requested due to automatic review settings July 31, 2026 03:35

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (2)

lib/core/widgets/app_webview.dart:136

  • onLoadStop always evaluates JavaScript to compute documentLength, even when the Windows nightly smoke logging is disabled. This adds unnecessary overhead for every WebView load (and can still throw/slow down pages) outside the smoke scenario.

Consider short-circuiting early when not running the Windows nightly smoke so the JS evaluation and related logging work only runs when it can actually be used.

        final uri = webUri == null ? null : Uri.tryParse(webUri.toString());
        var documentLength = -1;

.github/scripts/windows_payment_checkout_smoke.ps1:588

  • The transcript is started before generating the disposable user's password. PowerShell transcripts can capture executed lines, so this risks writing the plain-text password into the artifact logs.

Generate the password/credential before starting the transcript (and clear the plain-text variable) to avoid leaking it into logs.

    Start-Transcript -Path $transcript -Force | Out-Null
    $transcriptStarted = $true
    $passwordText = "L@ntern!" + ([Guid]::NewGuid().ToString("N"))
    $securePassword = ConvertTo-SecureString $passwordText -AsPlainText -Force

Copilot AI review requested due to automatic review settings July 31, 2026 05:59

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (1)

.github/scripts/windows_payment_checkout_smoke.ps1:485

  • Wait-CheckoutDocument can currently return success as soon as it sees a non-empty load_stop document from the expected host, even if the log already contains PAYMENT_WEBVIEW_SMOKE event=navigation_error / document_error (e.g., a main-frame HTTP error that still produced an HTML error page). This can incorrectly treat a checkout navigation error as a passing run and contradicts the smoke goal of "render without navigation errors".

Consider failing fast inside the polling loop whenever a navigation/document error is observed in the current log snapshot, before accepting load_stop.

      if ($lastLogText -match 'PAYMENT_CHECKOUT_SMOKE event=(rejected|bootstrap_error)') {
        throw "Lantern could not prepare the requested checkout smoke"
      }
      foreach ($match in [regex]::Matches($lastLogText, $linePattern)) {
        $hostName = $match.Groups[1].Value
        $documentLength = [int]$match.Groups[3].Value
        if ($hostName -match $HostPattern -and $documentLength -gt 0) {
          if ($lastLogText -notmatch 'PAYMENT_WEBVIEW_SMOKE event=created') {
            throw "The checkout page loaded without a WebView creation marker"
          }
          @{
            host = $hostName
            url = $match.Groups[2].Value
            documentLength = $documentLength
          } | ConvertTo-Json | Set-Content $ResultPath
          return
        }
      }

Copilot AI review requested due to automatic review settings July 31, 2026 07:56

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (2)

.github/scripts/windows_payment_checkout_smoke.ps1:744

  • The orchestrator treats process.json's isAdmin flag as an error, but that flag is computed from IsInRole(Administrator) in the launcher and can indicate group membership rather than an elevated token. Given the smoke user is intentionally added to Administrators for service access, this can incorrectly fail otherwise-non-elevated runs. Consider downgrading this to a warning (or removing it) and rely on the processIsElevated check instead.
    $diagnostics = Get-Content $diagnosticsPath -Raw | ConvertFrom-Json
    if ($diagnostics.isAdmin) {
      throw "The installed app was launched with an administrator token"
    }

.github/scripts/windows_payment_checkout_smoke.ps1:476

  • $isAdmin is derived from WindowsPrincipal.IsInRole(Administrator), which reflects Administrators-group membership (and can be true even when the process is running with a filtered/non-elevated UAC token). Since the script explicitly adds the disposable user to the Administrators group earlier, this check can cause false failures; the elevation check should rely on $processIsElevated (TokenElevation) instead.

This issue also appears on line 741 of the same file.

    $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = [Security.Principal.WindowsPrincipal]::new($identity)
    $isAdmin = $principal.IsInRole(
      [Security.Principal.WindowsBuiltInRole]::Administrator
    )
    $installWritePath = Join-Path (Split-Path $AppPath -Parent) (
      "smoke-write-" + [Guid]::NewGuid().ToString("N")
    )
    $canWriteInstallDirectory = $false
    try {
      New-Item -ItemType File -Path $installWritePath -Force `
        -ErrorAction Stop | Out-Null
      $canWriteInstallDirectory = $true
    } catch {
    } finally {
      Remove-Item $installWritePath -Force -ErrorAction SilentlyContinue
    }

    $localAppData = $env:LOCALAPPDATA
    $externalUDF = [Environment]::GetEnvironmentVariable(
      "WEBVIEW2_USER_DATA_FOLDER",
      "Process"
    )
    $app = Start-Process -FilePath $AppPath -ArgumentList @(
      "--payment-checkout-smoke=$CheckoutProvider",
      "--payment-checkout-run-id=$CheckoutRunID"
    ) -PassThru
    @{
      processId = $app.Id
      username = $identity.Name
      isAdmin = $isAdmin
      canWriteInstallDirectory = $canWriteInstallDirectory
      externalWebView2UserDataFolder = $externalUDF
      localAppData = $localAppData
      profilePath = $UserProfilePath
    } | ConvertTo-Json | Set-Content $ProcessDiagnosticsPath

    $app = Wait-ProcessMainWindow -ProcessID $app.Id -TimeoutSeconds 90
    $flutterViewHandle = Wait-FlutterViewHandle `
      -ParentHandle $app.MainWindowHandle -TimeoutSeconds 30
    $processIsElevated = [WindowsPaymentSmoke.NativeToken]::IsElevated($app.Id)
    $diagnostics = Get-Content $ProcessDiagnosticsPath -Raw | ConvertFrom-Json
    $diagnostics | Add-Member -NotePropertyName processIsElevated `
      -NotePropertyValue $processIsElevated
    $diagnostics | Add-Member -NotePropertyName mainWindowHandle `
      -NotePropertyValue ([IntPtr]$app.MainWindowHandle).ToInt64()
    $diagnostics | Add-Member -NotePropertyName flutterViewHandle `
      -NotePropertyValue $flutterViewHandle.ToInt64()
    $diagnostics | ConvertTo-Json | Set-Content $ProcessDiagnosticsPath
    if ($isAdmin -or $processIsElevated) {
      throw "The installed Lantern process has an elevated access token"
    }

Copilot AI review requested due to automatic review settings July 31, 2026 10:25

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (3)

.github/scripts/windows_payment_checkout_smoke.ps1:554

  • This re-reads WEBVIEW2_USER_DATA_FOLDER after it was cleared above, which overwrites the inherited value you want to record in process.json. After capturing the inherited value earlier, avoid reassigning $externalUDF here.
    $localAppData = $env:LOCALAPPDATA
    $externalUDF = [Environment]::GetEnvironmentVariable(
      "WEBVIEW2_USER_DATA_FOLDER",
      "Process"
    )

lib/core/widgets/app_webview.dart:192

  • onReceivedHttpError logs a main-frame navigation error but never stops the loading notifier or invokes _handleCompletionUrl, which can leave the WebView in a stuck loading state and skip existing completion handling logic for failures.
      onReceivedHttpError: (_, webResourceRequest, errorResponse) async {
        if (webResourceRequest.isForMainFrame != true) return;
        _logSmokeEvent(
          'navigation_error',
          Uri.tryParse(webResourceRequest.url.toString()),

.github/scripts/windows_payment_checkout_smoke.ps1:530

  • $externalUDF is read after WEBVIEW2_USER_DATA_FOLDER has already been cleared, so the diagnostics/validation around inherited overrides will always see an empty value. Capture the inherited value before clearing it.

This issue also appears on line 550 of the same file.

    [Environment]::SetEnvironmentVariable(
      "WEBVIEW2_USER_DATA_FOLDER",
      $null,
      [EnvironmentVariableTarget]::Process
    )

Copilot AI review requested due to automatic review settings July 31, 2026 11:52

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (2)

lib/core/widgets/app_webview.dart:205

  • _logSmokeEvent intends to log only the origin, but uri.replace(path: '', query: '', fragment: '') keeps an empty query/fragment as ?/# in the serialized URL. That’s noisy and can defeat strict log parsing/sanitization expectations; prefer clearing them with null so they are omitted entirely.
    // Checkout paths and query strings can contain session tokens. The origin
    // is enough to prove which provider loaded without putting them in CI logs.
    final safeUri = uri == null
        ? '<none>'
        : uri.replace(path: '', query: '', fragment: '').toString();
    final suffix = detail.isEmpty ? '' : ' $detail';

lib/features/auth/choose_payment_method.dart:613

  • Wrapping PrimaryButton in Semantics(excludeSemantics: true, ...) strips the button’s built-in semantics (role/actions). This can make the control non-operable or not announced as a button by assistive tech. Keep the automation identifier/name, but explicitly mark it as a button and provide an onTap action (or stop excluding child semantics).
              Semantics(
                identifier: 'payment-checkout-${method.providers.name}',
                label: 'Continue with $providerName',
                excludeSemantics: true,
                child: PrimaryButton(

Copilot AI review requested due to automatic review settings July 31, 2026 12:22

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

lib/core/widgets/app_webview.dart:137

  • evaluateJavascript and document-length parsing run unconditionally on every onLoadStop, even when smoke logging is disabled (non-Windows / non-nightly). This adds unnecessary overhead (and potential failures) to all in-app webviews; it should be gated to the same condition as _logSmokeEvent.
      onLoadStop: (controller, webUri) async {
        ref.read(webViewLoadingProvider.notifier).stop();
        final uri = webUri == null ? null : Uri.tryParse(webUri.toString());
        var documentLength = -1;
        try {

lib/features/auth/choose_payment_method.dart:613

  • Wrapping PrimaryButton in Semantics(excludeSemantics: true) removes the button's built-in semantics (role + activation action), which can break screen-reader interaction and makes the element harder to activate via accessibility APIs. If you need a stable label/identifier for automation, re-add button semantics + onTap at the wrapper level.
              Semantics(
                identifier: 'payment-checkout-${method.providers.name}',
                label: 'Continue with $providerName',
                excludeSemantics: true,
                child: PrimaryButton(
                  label: method.providers.supportSubscription
                      ? 'subscribe'.i18n
                      : 'checkout'.i18n,
                  enabled: !isSubmitting,
                  onPressed: () {
                    onSubscribe.call(method);
                  },
                ),
              ),

Comment on lines +202 to +204
final safeUri = uri == null
? '<none>'
: uri.replace(path: '', query: '', fragment: '').toString();
Copilot AI review requested due to automatic review settings July 31, 2026 12:56

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 13 changed files in this pull request and generated no new comments.

Suppressed comments (2)

lib/core/widgets/app_webview.dart:136

  • onLoadStop always calls evaluateJavascript to compute document length, even when the smoke logger is disabled (non-Windows or non-nightly builds). This adds unnecessary work (and potential failure noise) to every in-app webview navigation outside the smoke flow.

Gate the document-length probe behind the same Windows-nightly condition and early-return after _handleCompletionUrl when the smoke mode is not active.

        final uri = webUri == null ? null : Uri.tryParse(webUri.toString());
        var documentLength = -1;

lib/features/auth/choose_payment_method.dart:613

  • Wrapping PrimaryButton with Semantics(excludeSemantics: true) removes the button's semantics (role/action) unless the wrapper supplies them. As written, assistive technologies may not treat this as an actionable button, and activation via accessibility actions can stop working.

If you need a stable MSAA name, keep the wrapper but add the button role + tap action (and only expose the wrapper semantics).

              Semantics(
                identifier: 'payment-checkout-${method.providers.name}',
                label: 'Continue with $providerName',
                excludeSemantics: true,
                child: PrimaryButton(

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