import { expect } from "chai" ; import { mount , shallow } from "enzyme" ; import { fireEvent , render } from "@testing-library/react" ; import sinon from "sinon" ; import * as React from "react" ; import { BasePropertyEditorParams , InputEditorSizeParams , PropertyEditorParamTypes , RangeEditorParams , SpecialKey , StandardEditorNames , } from "@itwin/appui-abstract" ; import { NumericInputEditor } from "../../components-react/editors/NumericInputEditor" ; import TestUtils , { MineDataController } from "../TestUtils" ; import { EditorContainer , PropertyUpdatedArgs } from "../../components-react/editors/EditorContainer" ; import { PropertyEditorManager } from "../../components-react/editors/PropertyEditorManager" ; describe ( "<NumericInputEditor />" , ( ) => { before ( async ( ) => { await TestUtils . initializeUiComponents ( ) ; } ) ; beforeEach ( ( ) => { sinon . restore ( ) ; } ) ; after ( ( ) => { TestUtils . terminateUiComponents ( ) ; } ) ; it ( "should render" , ( ) => { mount ( < NumericInputEditor />); } ) ; it ( "renders correctly" , ( ) => { shallow ( < NumericInputEditor />).should.matchSnapshot(); } ) ; it ( "getValue returns proper value after componentDidMount & setState" , async ( ) => { const record = TestUtils . createNumericProperty ( "Test" , 123 , StandardEditorNames . NumericInput ) ; const wrapper = mount ( < NumericInputEditor propertyRecord = { record } / > ) ; await TestUtils . flushAsyncOperations ( ) ; const editor = wrapper . instance ( ) as NumericInputEditor ; expect ( editor . state . value ) . to . equal ( 123 ) ; wrapper . unmount ( ) ; } ) ; it ( "HTML input onChange updates value" , ( ) => { const record = TestUtils . createNumericProperty ( "Test1" , 5 , StandardEditorNames . NumericInput ) ; const component = render ( < NumericInputEditor propertyRecord = { record } / > ) ; const input = component . container . querySelector ( "input" ) as HTMLInputElement ; expect ( input . value ) . to . eq ( "5" ) ; const incrementor = component . container . querySelectorAll ( ".core-number-input-button" ) ; expect ( incrementor . length ) . to . eq ( 2 ) ; fireEvent . click ( incrementor [ 0 ] ) ; expect ( input . value ) . to . eq ( "6" ) ; } ) ; it ( "componentDidUpdate updates the value" , async ( ) => { const record = TestUtils . createNumericProperty ( "Test" , 123 , StandardEditorNames . NumericInput ) ; const wrapper = mount ( < NumericInputEditor propertyRecord = { record } / > ) ; await TestUtils . flushAsyncOperations ( ) ; const editor = wrapper . instance ( ) as NumericInputEditor ; expect ( editor . state . value ) . to . equal ( 123 ) ; const testValue = 987 ; const newRecord = TestUtils . createNumericProperty ( "Test" , testValue , StandardEditorNames . NumericInput ) ; wrapper . setProps ( { propertyRecord : newRecord } ) ; await TestUtils . flushAsyncOperations ( ) ; expect ( editor . state . value ) . to . equal ( testValue ) ; wrapper . unmount ( ) ; } ) ; it ( "should support InputEditorSize params" , async ( ) => { const size = 4 ; const maxLength = 60 ; const editorParams = [ ] ; const sizeParams = { type : PropertyEditorParamTypes . InputEditorSize , size , maxLength } ; editorParams . push ( sizeParams ) ; const record = TestUtils . createNumericProperty ( "Test" , 123 , StandardEditorNames . NumericInput , editorParams ) ; const wrapper = mount ( < NumericInputEditor propertyRecord = { record } / > ) ; await TestUtils . flushAsyncOperations ( ) ; const textEditor = wrapper . find ( NumericInputEditor ) ; expect ( textEditor . length ) . to . eq ( 1 ) ; expect ( textEditor . state ( "size" ) ) . to . eq ( size ) ; expect ( textEditor . state ( "maxLength" ) ) . to . eq ( maxLength ) ; wrapper . unmount ( ) ; } ) ; it ( "should support Range Editor Params" , async ( ) => { const editorParams = [ ] ; const rangeParams = { type : PropertyEditorParamTypes . Range , minimum : 1 , maximum : 100 , step : 5 , precision : 2 } ; editorParams . push ( rangeParams ) ; const record = TestUtils . createNumericProperty ( "Test" , 123 , StandardEditorNames . NumericInput , editorParams ) ; const wrapper = mount ( < NumericInputEditor propertyRecord = { record } / > ) ; await TestUtils . flushAsyncOperations ( ) ; const textEditor = wrapper . find ( NumericInputEditor ) ; expect ( textEditor . length ) . to . eq ( 1 ) ; expect ( textEditor . state ( "min" ) ) . to . eq ( 1 ) ; expect ( textEditor . state ( "max" ) ) . to . eq ( 100 ) ; expect ( textEditor . state ( "step" ) ) . to . eq ( 5 ) ; expect ( textEditor . state ( "precision" ) ) . to . eq ( 2 ) ; wrapper . unmount ( ) ; } ) ; it ( "renders editor for 'number' type and 'numeric-input' editor using NumericInputEditor" , ( ) => { const propertyRecord = TestUtils . createNumericProperty ( "Test" , 123 , StandardEditorNames . NumericInput ) ; const renderedComponent = render ( < EditorContainer propertyRecord = { propertyRecord } title = "abc" onCommit = { ( ) => { } } onCancel = { ( ) => { } } />); expect ( renderedComponent . container . querySelector ( ".components-numeric-input-editor" ) ) . to . not . be . empty ; } ) ; it ( "calls onCommit for Enter" , async ( ) => { const propertyRecord = TestUtils . createNumericProperty ( "Test" , 123 , StandardEditorNames . NumericInput ) ; const spyOnCommit = sinon . spy ( ) ; function handleCommit ( _commit ) { spyOnCommit ( ) ; } const wrapper = render ( < EditorContainer propertyRecord = { propertyRecord } title = "abc" onCommit = { handleCommit } onCancel = { ( ) => { } } />); const inputNode = wrapper . container . querySelector ( "input" ) ; expect ( inputNode ) . not . to . be . null ; fireEvent . keyDown ( inputNode as HTMLElement , { key : SpecialKey . Enter } ) ; await TestUtils . flushAsyncOperations ( ) ; expect ( spyOnCommit . calledOnce ) . to . be . true ; } ) ; it ( "calls onCommit on increment click" , async ( ) => { const propertyRecord = TestUtils . createNumericProperty ( "Test" , 123 , StandardEditorNames . NumericInput ) ; const spyOnCommit = sinon . spy ( ) ; function handleCommit ( _commit ) { spyOnCommit ( ) ; } const wrapper = render ( < EditorContainer propertyRecord = { propertyRecord } title = "abc" onCommit = { handleCommit } onCancel = { ( ) => { } } />); const inputNode = wrapper . container . querySelector ( "input" ) ; expect ( inputNode ) . not . to . be . null ; const input = wrapper . container . querySelector ( "input" ) as HTMLInputElement ; const incrementor = wrapper . container . querySelectorAll ( ".core-number-input-button" ) ; expect ( incrementor . length ) . to . eq ( 2 ) ; fireEvent . click ( incrementor [ 0 ] ) ; await TestUtils . flushAsyncOperations ( ) ; expect ( spyOnCommit . calledOnce ) . to . be . true ; expect ( input . value ) . to . eq ( "124" ) ; } ) ; it ( "should not commit if DataController fails to validate" , async ( ) => { PropertyEditorManager . registerDataController ( "myData" , MineDataController ) ; const record = TestUtils . createNumericProperty ( "Test" , 123 , StandardEditorNames . NumericInput ) ; record . property . dataController = "myData" ; const spyOnCommit = sinon . spy ( ) ; const spyOnCancel = sinon . spy ( ) ; const renderedComponent = render ( < EditorContainer propertyRecord = { record } title = "abc" onCommit = { spyOnCommit } onCancel = { spyOnCancel } / > ) ; expect ( renderedComponent ) . not . to . be . undefined ; const inputNode = renderedComponent . container . querySelector ( "input" ) ; expect ( inputNode ) . not . to . be . null ; fireEvent . keyDown ( inputNode as HTMLElement , { key : SpecialKey . Enter } ) ; await TestUtils . flushAsyncOperations ( ) ; expect ( spyOnCommit . called ) . to . be . false ; fireEvent . keyDown ( inputNode as HTMLElement , { key : SpecialKey . Escape } ) ; await TestUtils . flushAsyncOperations ( ) ; expect ( spyOnCancel . calledOnce ) . to . be . true ; PropertyEditorManager . deregisterDataController ( "myData" ) ; } ) ; } ) ;