|
5 | 5 | buildInteractionSurfaceSignature, |
6 | 6 | classifyBaselineSurfaceEvidence, |
7 | 7 | classifyInteractionSurfaceChange, |
| 8 | + discriminatingSurfaceChangedWithinRect, |
8 | 9 | markPendingInteractionOutcome, |
9 | 10 | stripInternalInteractionFlags, |
10 | 11 | } from '../interaction-outcome-policy.ts'; |
@@ -411,3 +412,58 @@ function makeSnapshotWithExtraText(label: string, y = 100): SnapshotState { |
411 | 412 | ], |
412 | 413 | }; |
413 | 414 | } |
| 415 | + |
| 416 | +// --------------------------------------------------------------------------- |
| 417 | +// discriminatingSurfaceChangedWithinRect (#2714 review): a whole-surface |
| 418 | +// difference that lives outside the region a command acted on is not that |
| 419 | +// command's doing. Measured on the Android tester, the status bar changed on |
| 420 | +// its own over a list that never moved. The strict key-matched view stays |
| 421 | +// owned here; this only narrows the region it is asked about. |
| 422 | +// --------------------------------------------------------------------------- |
| 423 | + |
| 424 | +const LIST_RECT = { x: 0, y: 150, width: 390, height: 600 }; |
| 425 | + |
| 426 | +function rowAt(index: number, y: number) { |
| 427 | + return { |
| 428 | + type: 'StaticText', |
| 429 | + identifier: `row-${index}`, |
| 430 | + label: `Row ${index}`, |
| 431 | + rect: { x: 12, y, width: 300, height: 24 }, |
| 432 | + } as SnapshotState['nodes'][number]; |
| 433 | +} |
| 434 | + |
| 435 | +function surfaceWithClock(clock: string, secondRowY: number) { |
| 436 | + return [ |
| 437 | + { |
| 438 | + type: 'Image', |
| 439 | + identifier: 'status-clock', |
| 440 | + label: clock, |
| 441 | + rect: { x: 12, y: 12, width: 40, height: 18 }, |
| 442 | + } as SnapshotState['nodes'][number], |
| 443 | + rowAt(1, 200), |
| 444 | + rowAt(2, secondRowY), |
| 445 | + ]; |
| 446 | +} |
| 447 | + |
| 448 | +test('discriminatingSurfaceChangedWithinRect reads a movement inside the region', () => { |
| 449 | + const before = buildInteractionSurfaceSignature(surfaceWithClock('2:40', 300)); |
| 450 | + const after = buildInteractionSurfaceSignature(surfaceWithClock('2:40', 420)); |
| 451 | + |
| 452 | + assert.equal(discriminatingSurfaceChangedWithinRect(before, after, LIST_RECT), true); |
| 453 | +}); |
| 454 | + |
| 455 | +test('discriminatingSurfaceChangedWithinRect does not read a movement outside the region', () => { |
| 456 | + const before = buildInteractionSurfaceSignature(surfaceWithClock('2:40', 300)); |
| 457 | + const after = buildInteractionSurfaceSignature(surfaceWithClock('2:41', 300)); |
| 458 | + |
| 459 | + // The whole surface did change — that is the trap this reader exists to refuse. |
| 460 | + assert.equal(classifyBaselineSurfaceEvidence(before, after), 'changed'); |
| 461 | + assert.equal(discriminatingSurfaceChangedWithinRect(before, after, LIST_RECT), false); |
| 462 | +}); |
| 463 | + |
| 464 | +test('discriminatingSurfaceChangedWithinRect counts content appearing inside the region', () => { |
| 465 | + const before = buildInteractionSurfaceSignature(surfaceWithClock('2:40', 300)); |
| 466 | + const after = buildInteractionSurfaceSignature([...surfaceWithClock('2:40', 300), rowAt(3, 640)]); |
| 467 | + |
| 468 | + assert.equal(discriminatingSurfaceChangedWithinRect(before, after, LIST_RECT), true); |
| 469 | +}); |
0 commit comments