11import '@angular/compiler' ;
22import { describe , it , expect , vi , beforeEach , afterEach } from 'vitest' ;
33import { Subject , Subscription } from 'rxjs' ;
4- import { debounceTime , distinctUntilChanged } from 'rxjs/operators' ;
4+ import { debounceTime } from 'rxjs/operators' ;
55import { TestBed } from '@angular/core/testing' ;
66import { ComponentFixture } from '@angular/core/testing' ;
77
88import { OptionComponent , OptionType , OptionValue , DEBOUNCE_TIME_MS } from './option.component' ;
99import { REDACTED_SENTINEL } from '../../models/config' ;
1010
1111/**
12- * Test the debounce/distinctUntilChanged pipe logic directly using the same
13- * operators as OptionComponent, avoiding Angular component lifecycle timing issues.
12+ * Test the debounce pipe logic directly using the same operator as
13+ * OptionComponent, avoiding Angular component lifecycle timing issues.
1414 */
1515describe ( 'OptionComponent — debounce pipe logic' , ( ) => {
1616 let newValue : Subject < OptionValue > ;
@@ -22,7 +22,7 @@ describe('OptionComponent — debounce pipe logic', () => {
2222 newValue = new Subject < OptionValue > ( ) ;
2323 emitted = [ ] ;
2424 subscription = newValue
25- . pipe ( debounceTime ( DEBOUNCE_TIME_MS ) , distinctUntilChanged ( ) )
25+ . pipe ( debounceTime ( DEBOUNCE_TIME_MS ) )
2626 . subscribe ( ( val ) => emitted . push ( val ) ) ;
2727 } ) ;
2828
@@ -52,16 +52,6 @@ describe('OptionComponent — debounce pipe logic', () => {
5252 expect ( emitted ) . toEqual ( [ 'c' ] ) ;
5353 } ) ;
5454
55- it ( 'should not re-emit same value after full debounce period (distinctUntilChanged)' , ( ) => {
56- newValue . next ( 'same' ) ;
57- vi . advanceTimersByTime ( DEBOUNCE_TIME_MS ) ;
58- expect ( emitted ) . toEqual ( [ 'same' ] ) ;
59-
60- newValue . next ( 'same' ) ;
61- vi . advanceTimersByTime ( DEBOUNCE_TIME_MS ) ;
62- expect ( emitted ) . toEqual ( [ 'same' ] ) ;
63- } ) ;
64-
6555 it ( 'should emit different values after each debounce period' , ( ) => {
6656 newValue . next ( 'first' ) ;
6757 vi . advanceTimersByTime ( DEBOUNCE_TIME_MS ) ;
@@ -92,6 +82,30 @@ describe('OptionComponent — onChange and effectiveChoices', () => {
9282 vi . useRealTimers ( ) ;
9383 } ) ;
9484
85+ it ( 'emits only when the typed value differs from the bound value, even after an external change' , ( ) => {
86+ vi . useFakeTimers ( ) ;
87+ fixture . componentRef . setInput ( 'value' , '/a' ) ;
88+ fixture . detectChanges ( ) ;
89+ const emitted : OptionValue [ ] = [ ] ;
90+ component . changeEvent . subscribe ( ( v ) => emitted . push ( v ) ) ;
91+
92+ component . onChange ( '/a' ) ;
93+ vi . advanceTimersByTime ( DEBOUNCE_TIME_MS ) ;
94+ expect ( emitted ) . toEqual ( [ ] ) ;
95+
96+ component . onChange ( '/b' ) ;
97+ vi . advanceTimersByTime ( DEBOUNCE_TIME_MS ) ;
98+ expect ( emitted ) . toEqual ( [ '/b' ] ) ;
99+
100+ // The directory picker saves '/x' through a different path; typing '/b'
101+ // again is a real change now and must not be swallowed.
102+ fixture . componentRef . setInput ( 'value' , '/x' ) ;
103+ fixture . detectChanges ( ) ;
104+ component . onChange ( '/b' ) ;
105+ vi . advanceTimersByTime ( DEBOUNCE_TIME_MS ) ;
106+ expect ( emitted ) . toEqual ( [ '/b' , '/b' ] ) ;
107+ } ) ;
108+
95109 it ( 'should suppress REDACTED_SENTINEL for password type' , ( ) => {
96110 fixture . componentRef . setInput ( 'type' , OptionType . Password ) ;
97111 fixture . detectChanges ( ) ;
0 commit comments