Skip to content

fix(picker): stage setX during lightweight popup and roll back on Cancel#4923

Merged
liannacasper merged 3 commits into
masterfrom
picker-stage-cancel-4897
May 12, 2026
Merged

fix(picker): stage setX during lightweight popup and roll back on Cancel#4923
liannacasper merged 3 commits into
masterfrom
picker-stage-cancel-4897

Conversation

@shai-almog
Copy link
Copy Markdown
Collaborator

Summary

  • Follow-up to RFE: Add method to Picker to get or update internal value based on the Picker scroll wheel positions set by the user #4897: custom popup buttons that mutate the picker mid-edit (e.g. a +7 Days action calling setDate(getDate() + 7d)) wrote the staged value directly into the committed value field. Pressing Cancel then leaked that value into the picker instead of rolling back to the date the picker held before editing began.
  • Snapshot value into a new preEditValue field when the lightweight popup is shown, and roll back to it in endEditing when the user presses Cancel. Done / Next / Prev still commit the wheel value as before and clear the snapshot on the way out.
  • Native pickers are unchanged — they are read-only while shown, which the existing setter Javadocs already call out; the new staging semantics apply only to the lightweight popup.
  • Setter Javadocs (setDate, setTime, setDuration, setSelectedString, setSelectedStringIndex) updated to describe the staging behavior.

Test plan

  • PickerCancelRestoreTest drives a Picker with a +7 Days custom button programmatically: opens the popup, clicks the button, then clicks Cancel and asserts getDate() returns the pre-edit date. Repeats with Done and asserts the staged date is committed. Registered in Cn1ssDeviceRunner.
  • Existing LightweightPickerButtonsScreenshotTest continues to exercise the live wheel propagation from RFE: Add method to Picker to get or update internal value based on the Picker scroll wheel positions set by the user #4897 (verified by walking the test flow — stopEditing doesn't go through the cancel path, so the staged wheel value still drives the screenshot).
  • Run the JS / Android / iOS device suite to confirm no regressions on the picker popup lifecycle.

🤖 Generated with Claude Code

Follow-up to #4897: custom popup buttons that mutate the picker mid-edit
(e.g. "+7 days" calling setDate(getDate() + 7d)) wrote the staged value
straight into the committed `value` field. If the user then dismissed
the popup with Cancel, getDate() returned the staged value instead of
the date the picker held before editing began, leaking the unwanted
change into the picker.

Snapshot `value` into a new preEditValue field at the moment the
lightweight popup is shown, then roll back to it in endEditing when the
user presses Cancel. Done / Next / Prev still commit the wheel value as
before, and clear the snapshot on the way out. Native pickers are
unchanged - they are read-only while shown, which the existing setter
Javadocs already call out; the new staging semantics only apply to the
lightweight popup.

Adds PickerCancelRestoreTest: drives a Picker with a "+7 Days" custom
button programmatically, asserts that Cancel restores the pre-edit
date and that Done commits the staged date. Registered in
Cn1ssDeviceRunner alongside the existing lightweight-picker tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 12, 2026

Compared 105 screenshots: 105 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 209 seconds

Build and Run Timing

Metric Duration
Simulator Boot 72000 ms
Simulator Boot (Run) 1000 ms
App Install 13000 ms
App Launch 4000 ms
Test Execution 250000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1811.000 ms
Base64 CN1 encode 1823.000 ms
Base64 encode ratio (CN1/native) 1.007x (0.7% slower)
Base64 native decode 1471.000 ms
Base64 CN1 decode 1862.000 ms
Base64 decode ratio (CN1/native) 1.266x (26.6% slower)
Base64 SIMD encode 783.000 ms
Base64 encode ratio (SIMD/native) 0.432x (56.8% faster)
Base64 encode ratio (SIMD/CN1) 0.430x (57.0% faster)
Base64 SIMD decode 663.000 ms
Base64 decode ratio (SIMD/native) 0.451x (54.9% faster)
Base64 decode ratio (SIMD/CN1) 0.356x (64.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 274.000 ms
Image createMask (SIMD on) 181.000 ms
Image createMask ratio (SIMD on/off) 0.661x (33.9% faster)
Image applyMask (SIMD off) 615.000 ms
Image applyMask (SIMD on) 255.000 ms
Image applyMask ratio (SIMD on/off) 0.415x (58.5% faster)
Image modifyAlpha (SIMD off) 511.000 ms
Image modifyAlpha (SIMD on) 154.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.301x (69.9% faster)
Image modifyAlpha removeColor (SIMD off) 495.000 ms
Image modifyAlpha removeColor (SIMD on) 316.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.638x (36.2% faster)
Image PNG encode (SIMD off) 1721.000 ms
Image PNG encode (SIMD on) 1179.000 ms
Image PNG encode ratio (SIMD on/off) 0.685x (31.5% faster)
Image JPEG encode 690.000 ms

…ts own popup

The follow-up cancel/done test caught a pre-existing bug exposed once
the popup was reopened after a Cancel. endEditing nulls Picker's local
currentInput field but leaves Form.currentInputDevice pointing at the
just-disposed VirtualInputDevice. When the next showInteractionDialog
reassigns Picker.currentSpinner to the freshly created spinner and
calls Form.setCurrentInputDevice(newInput), the form synchronously
calls close() on the OLD VirtualInputDevice -- whose close() was
unconditionally setting currentSpinner = null. That clobbered the new
popup's live wheel reference, so setX calls from custom buttons no
longer propagated and Done committed the spinner's initial value.

Capture the spinner in registerAsInputDevice and only null
currentSpinner / currentInput in close() when they still point at THIS
popup, mirroring the same compare-then-null pattern endEditing already
uses. Single-popup flows (initial open, Done, Cancel, stopEditing) are
unaffected because the fields still match THIS device when close()
fires.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 12, 2026

Compared 105 screenshots: 105 matched.

Native Android coverage

  • 📊 Line coverage: 11.25% (6217/55280 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.94% (30803/344403), branch 3.86% (1265/32764), complexity 4.98% (1564/31415), method 8.72% (1282/14698), class 14.77% (294/1991)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 11.25% (6217/55280 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 8.94% (30803/344403), branch 3.86% (1265/32764), complexity 4.98% (1564/31415), method 8.72% (1282/14698), class 14.77% (294/1991)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 922.000 ms
Base64 CN1 encode 210.000 ms
Base64 encode ratio (CN1/native) 0.228x (77.2% faster)
Base64 native decode 860.000 ms
Base64 CN1 decode 209.000 ms
Base64 decode ratio (CN1/native) 0.243x (75.7% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 12, 2026

Compared 105 screenshots: 105 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 195 seconds

Build and Run Timing

Metric Duration
Simulator Boot 118000 ms
Simulator Boot (Run) 0 ms
App Install 18000 ms
App Launch 10000 ms
Test Execution 331000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1614.000 ms
Base64 CN1 encode 1443.000 ms
Base64 encode ratio (CN1/native) 0.894x (10.6% faster)
Base64 native decode 780.000 ms
Base64 CN1 decode 1689.000 ms
Base64 decode ratio (CN1/native) 2.165x (116.5% slower)
Base64 SIMD encode 708.000 ms
Base64 encode ratio (SIMD/native) 0.439x (56.1% faster)
Base64 encode ratio (SIMD/CN1) 0.491x (50.9% faster)
Base64 SIMD decode 511.000 ms
Base64 decode ratio (SIMD/native) 0.655x (34.5% faster)
Base64 decode ratio (SIMD/CN1) 0.303x (69.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 71.000 ms
Image createMask (SIMD on) 9.000 ms
Image createMask ratio (SIMD on/off) 0.127x (87.3% faster)
Image applyMask (SIMD off) 145.000 ms
Image applyMask (SIMD on) 104.000 ms
Image applyMask ratio (SIMD on/off) 0.717x (28.3% faster)
Image modifyAlpha (SIMD off) 176.000 ms
Image modifyAlpha (SIMD on) 72.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.409x (59.1% faster)
Image modifyAlpha removeColor (SIMD off) 199.000 ms
Image modifyAlpha removeColor (SIMD on) 109.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.548x (45.2% faster)
Image PNG encode (SIMD off) 1313.000 ms
Image PNG encode (SIMD on) 1126.000 ms
Image PNG encode ratio (SIMD on/off) 0.858x (14.2% faster)
Image JPEG encode 869.000 ms

…che hit

The cache key was fixed at v1 while npm install playwright resolves the
floating latest. When Playwright shipped a new chromium build between
runs, the cached browser binary stayed at the older revision but the
freshly installed Playwright pointed at a path that didn't exist
(chromium_headless_shell-1223/...), so every CI run since the drift
failed with "browserType.launch: Executable doesn't exist" before any
test could start.

Always run `npx playwright install chromium` after npm install. It's a
no-op when the cached binary matches and re-downloads when versions
drift, so the workflow self-heals on the next Playwright bump. OS deps
still install unconditionally (they were never cached). Bumps the cache
key to v2 so the bad entry gets rebuilt instead of restored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shai-almog
Copy link
Copy Markdown
Collaborator Author

shai-almog commented May 12, 2026

Compared 15 screenshots: 15 matched.
✅ JavaScript-port screenshot tests passed.

@liannacasper liannacasper merged commit 6bc2988 into master May 12, 2026
21 checks passed
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