From 68168505ad3032635b69a3d1308048742e0e96dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:03:44 +0000 Subject: [PATCH 1/7] Initial plan for issue From f5cbdf0cf6918fdbac25c85337c505126f10459c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:12:51 +0000 Subject: [PATCH 2/7] Add functional tests for TextInput editable, maxLength, multiline, onPressIn/Out events Co-authored-by: anupriya13 <54227869+anupriya13@users.noreply.github.com> --- .../test/LegacyTextInputTest.test.ts | 32 +++++ .../test/TextInputComponentTest.test.ts | 130 ++++++++++++++++++ 2 files changed, 162 insertions(+) diff --git a/packages/e2e-test-app-fabric/test/LegacyTextInputTest.test.ts b/packages/e2e-test-app-fabric/test/LegacyTextInputTest.test.ts index da49f5c3b10..ee1205ca28f 100644 --- a/packages/e2e-test-app-fabric/test/LegacyTextInputTest.test.ts +++ b/packages/e2e-test-app-fabric/test/LegacyTextInputTest.test.ts @@ -98,6 +98,38 @@ describe('LegacyTextInputTest', () => { ]); */ }); + + // Additional functional tests for event handlers + test('TextInput should trigger action upon onBlur', async () => { + const textInput = await textInputField(); + await textInput.click(); // Focus + + const multiLineTextInput = await multiLineTextInputField(); + await multiLineTextInput.click(); // This should trigger onBlur on the first TextInput + + await assertLogContains('onBlur'); + }); + + test('TextInput should trigger action upon onFocus', async () => { + const textInput = await textInputField(); + await textInput.click(); // This should trigger onFocus + + await assertLogContains('onFocus'); + }); + + test('TextInput should trigger action upon onChange', async () => { + const textInput = await textInputField(); + await textInput.setValue('test'); + + await assertLogContains('onChange text: test'); + }); + + test('TextInput should trigger action upon onSelectionChange', async () => { + const textInput = await textInputField(); + await textInput.setValue('test'); + + await assertLogContains('onSelectionChange range: 4,4'); + }); }); async function textInputField() { diff --git a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts index 2f2eadbf835..7a4fe58fd9b 100644 --- a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts +++ b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts @@ -953,4 +953,134 @@ describe('TextInput Tests', () => { const dump = await dumpVisualTree('textinput-searchbox'); expect(dump).toMatchSnapshot(); }); + + // Additional functional tests for specific task requirements + test('TextInput should not be editable when editable set to false', async () => { + const component = await app.findElementByTestID('textinput-not-editable2'); + await component.waitForDisplayed({timeout: 5000}); + const dump = await dumpVisualTree('textinput-not-editable2'); + expect(dump).toMatchSnapshot(); + + // Attempt to set value and verify it doesn't work + const originalText = await component.getText(); + await component.setValue('Should not work'); + expect(await component.getText()).toBe(originalText); + }); + + test('TextInput should take up to max length input when maxLength set', async () => { + const component = await app.findElementByTestID('rewrite_sp_underscore_input'); + await component.waitForDisplayed({timeout: 5000}); + const dump = await dumpVisualTree('rewrite_sp_underscore_input'); + expect(dump).toMatchSnapshot(); + + // Test that input is limited by maxLength (this component has maxLength=20) + await app.waitUntil( + async () => { + await component.setValue('This is a very long text that should be truncated because it exceeds the limit'); + const text = await component.getText(); + // The component replaces spaces with underscores and has maxLength=20 + return text.length <= 20; + }, + { + interval: 1500, + timeout: 5000, + timeoutMsg: `MaxLength limitation not working correctly.`, + }, + ); + + // Verify that the text was actually limited + const finalText = await component.getText(); + expect(finalText.length).toBeLessThanOrEqual(20); + }); + + test('TextInput input should wrap to multiple lines when multiline set to true', async () => { + const component = await app.findElementByTestID('textinput-multiline-topleft'); + await component.waitForDisplayed({timeout: 5000}); + const dump = await dumpVisualTree('textinput-multiline-topleft'); + expect(dump).toMatchSnapshot(); + + // Set a long text that should wrap to multiple lines + await app.waitUntil( + async () => { + await component.setValue('This is a very long text that should wrap to multiple lines when the multiline property is set to true.'); + return (await component.getText()).includes('This is a very long text'); + }, + { + interval: 1500, + timeout: 5000, + timeoutMsg: `Unable to enter text in multiline TextInput.`, + }, + ); + }); + + test('TextInput should not be editable when readOnly set to true', async () => { + const component = await app.findElementByTestID('textinput-readyonly'); + await component.waitForDisplayed({timeout: 5000}); + const dump = await dumpVisualTree('textinput-readyonly'); + expect(dump).toMatchSnapshot(); + + // Attempt to set value and verify it doesn't work + const originalText = await component.getText(); + await component.setValue('Should not work'); + expect(await component.getText()).toBe(originalText); + }); + + test('TextInput should trigger action upon onPressIn', async () => { + // Using the existing textinput-press component which handles onPressIn + await searchBox('onPressIn'); + const component = await app.findElementByTestID('textinput-press'); + await component.waitForDisplayed({timeout: 5000}); + const dump = await dumpVisualTree('textinput-press'); + expect(dump).toMatchSnapshot(); + + // Trigger onPressIn by clicking + await component.click(); + const stateText = await app.findElementByTestID('textinput-state-display'); + + await app.waitUntil( + async () => { + const currentText = await stateText.getText(); + return currentText === 'Holding down the click/touch'; + }, + { + timeout: 5000, + timeoutMsg: 'onPressIn event not triggered correctly.', + }, + ); + + expect(await stateText.getText()).toBe('Holding down the click/touch'); + // Reset search to avoid interfering with other tests + const search = await app.findElementByTestID('example_search'); + await search.setValue(''); + }); + + test('TextInput should trigger action upon onPressOut', async () => { + // Using the existing textinput-press component which handles onPressOut + await searchBox('onPressIn'); + const component = await app.findElementByTestID('textinput-press'); + await component.waitForDisplayed({timeout: 5000}); + const stateText = await app.findElementByTestID('textinput-state-display'); + + // Trigger onPressIn first, then onPressOut + await component.click(); // This should trigger onPressIn + + // Move to another element to trigger onPressOut + const search = await app.findElementByTestID('example_search'); + await search.click(); + + await app.waitUntil( + async () => { + const currentText = await stateText.getText(); + return currentText === 'Released click/touch'; + }, + { + timeout: 5000, + timeoutMsg: 'onPressOut event not triggered correctly.', + }, + ); + + expect(await stateText.getText()).toBe('Released click/touch'); + // Reset search to avoid interfering with other tests + await search.setValue(''); + }); }); From a0705b1f06c2ff69b0d45e7f4fdc1ac09260d90a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:15:34 +0000 Subject: [PATCH 3/7] Add comprehensive TextInput functional tests including clear, focus, blur, isFocused, and value prop tests Co-authored-by: anupriya13 <54227869+anupriya13@users.noreply.github.com> --- .../test/TextInputComponentTest.test.ts | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts index 7a4fe58fd9b..d327cee3e46 100644 --- a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts +++ b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts @@ -1083,4 +1083,141 @@ describe('TextInput Tests', () => { // Reset search to avoid interfering with other tests await search.setValue(''); }); + + test('TextInput text should clear upon clear() call', async () => { + // Using the rewrite example which has a clear button + const component = await app.findElementByTestID('rewrite_clear_input'); + await component.waitForDisplayed({timeout: 5000}); + const dump = await dumpVisualTree('rewrite_clear_input'); + expect(dump).toMatchSnapshot(); + + // Set some text first + await app.waitUntil( + async () => { + await component.setValue('Hello World'); + return (await component.getText()) === 'HelloWorld'; // Spaces are removed in this component + }, + { + interval: 1500, + timeout: 5000, + timeoutMsg: `Unable to enter correct text.`, + }, + ); + + // Click the clear button to test clear() method + const clearButton = await app.findElementByTestID('rewrite_clear_button'); + await clearButton.click(); + + // Verify text was cleared + await app.waitUntil( + async () => { + return (await component.getText()) === ''; + }, + { + interval: 1500, + timeout: 5000, + timeoutMsg: `Clear method did not work correctly.`, + }, + ); + + expect(await component.getText()).toBe(''); + }); + + test('TextInput value prop should be the text displayed in the TextInput', async () => { + // Using the rewrite example which uses a controlled value prop + const component = await app.findElementByTestID('rewrite_sp_underscore_input'); + await component.waitForDisplayed({timeout: 5000}); + const dump = await dumpVisualTree('rewrite_sp_underscore_input'); + expect(dump).toMatchSnapshot(); + + // Test that the value prop controls what's displayed + await app.waitUntil( + async () => { + await component.setValue('test value'); + // This component replaces spaces with underscores + return (await component.getText()) === 'test_value'; + }, + { + interval: 1500, + timeout: 5000, + timeoutMsg: `Value prop not working correctly.`, + }, + ); + + expect(await component.getText()).toBe('test_value'); + }); + + test('TextInput should focus upon .focus() call', async () => { + // Test focus behavior using the uncontrolled component which changes style on focus + const component = await app.findElementByTestID('uncontrolled-textinput'); + await component.waitForDisplayed({timeout: 5000}); + + // Initial state - not focused + let dump = await dumpVisualTree('uncontrolled-textinput'); + expect(dump).toMatchSnapshot(); + + // Simulate focus by clicking + await component.click(); + + // After focus, the style should change (component has onFocus handler) + dump = await dumpVisualTree('uncontrolled-textinput'); + expect(dump).toMatchSnapshot(); + }); + + test('TextInput should lose focus upon .blur() call', async () => { + // Test blur behavior using the uncontrolled component + const component = await app.findElementByTestID('uncontrolled-textinput'); + await component.waitForDisplayed({timeout: 5000}); + + // Focus first + await component.click(); + + // Then blur by clicking somewhere else + const search = await app.findElementByTestID('example_search'); + await search.click(); + + // After blur, the style should revert (component has onBlur handler) + const dump = await dumpVisualTree('uncontrolled-textinput'); + expect(dump).toMatchSnapshot(); + + // Reset search + await search.setValue(''); + }); + + test('TextInput isFocused() should return true when the TextInput is focused', async () => { + // Simulate isFocused behavior using the uncontrolled component's style changes + const component = await app.findElementByTestID('uncontrolled-textinput'); + await component.waitForDisplayed({timeout: 5000}); + + // Focus the component + await component.click(); + + // Check if the component shows focused state (style change indicates focus) + const dump = await dumpVisualTree('uncontrolled-textinput'); + expect(dump).toMatchSnapshot(); + + // The component should be in focused state (we can't directly test isFocused() + // but we can verify the visual state that indicates focus) + expect(await component.isFocused()).toBe(true); + }); + + test('TextInput isFocused() should return false when the TextInput is not focused', async () => { + // Simulate isFocused behavior using the uncontrolled component + const component = await app.findElementByTestID('uncontrolled-textinput'); + await component.waitForDisplayed({timeout: 5000}); + + // Ensure component is not focused by clicking elsewhere + const search = await app.findElementByTestID('example_search'); + await search.click(); + + // Check if the component shows unfocused state + const dump = await dumpVisualTree('uncontrolled-textinput'); + expect(dump).toMatchSnapshot(); + + // The component should not be in focused state + expect(await component.isFocused()).toBe(false); + + // Reset search + await search.setValue(''); + }); }); From 884bc9ea04b510bcd345235eb416392dee824b98 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:37:07 +0000 Subject: [PATCH 4/7] Add change file for TextInput functional tests Co-authored-by: anupriya13 <54227869+anupriya13@users.noreply.github.com> --- ...ative-windows-37346184-096b-45d7-9181-78fb1afdd641.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json diff --git a/change/react-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json b/change/react-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json new file mode 100644 index 00000000000..49c1662c52c --- /dev/null +++ b/change/react-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Add comprehensive functional tests for TextInput component in E2E test app (Fabric)", + "packageName": "react-native-windows", + "email": "copilot@github.com", + "dependentChangeType": "patch" +} \ No newline at end of file From 6fc46e2b0092238309fef6bfa0bf121bc379613c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Jun 2025 09:48:06 +0000 Subject: [PATCH 5/7] Fix change file type from 'patch' to 'prerelease' for validation Co-authored-by: anupriya13 <54227869+anupriya13@users.noreply.github.com> --- ...act-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/react-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json b/change/react-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json index 49c1662c52c..d4df42cd0bf 100644 --- a/change/react-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json +++ b/change/react-native-windows-37346184-096b-45d7-9181-78fb1afdd641.json @@ -1,5 +1,5 @@ { - "type": "patch", + "type": "prerelease", "comment": "Add comprehensive functional tests for TextInput component in E2E test app (Fabric)", "packageName": "react-native-windows", "email": "copilot@github.com", From e06b6db87392285a64a2933e45590f979bcf0612 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Jun 2025 20:28:40 +0000 Subject: [PATCH 6/7] Add dumpVisualTree snapshot after text input in multiline test Co-authored-by: chiaramooney <34109996+chiaramooney@users.noreply.github.com> --- .../e2e-test-app-fabric/test/TextInputComponentTest.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts index d327cee3e46..f88bee02022 100644 --- a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts +++ b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts @@ -1011,6 +1011,10 @@ describe('TextInput Tests', () => { timeoutMsg: `Unable to enter text in multiline TextInput.`, }, ); + + // Capture visual tree after text has been set to validate wrapping + const dumpAfterText = await dumpVisualTree('textinput-multiline-topleft'); + expect(dumpAfterText).toMatchSnapshot(); }); test('TextInput should not be editable when readOnly set to true', async () => { From 4363ed31fe482b305bcfc2fea40582d3b8a7f9ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Jun 2025 20:42:12 +0000 Subject: [PATCH 7/7] Address review feedback: remove legacy tests, fix focus/blur API usage, clean up isFocused tests Co-authored-by: chiaramooney <34109996+chiaramooney@users.noreply.github.com> --- .../test/LegacyTextInputTest.test.ts | 32 ------------------- .../test/TextInputComponentTest.test.ts | 21 +++--------- 2 files changed, 4 insertions(+), 49 deletions(-) diff --git a/packages/e2e-test-app-fabric/test/LegacyTextInputTest.test.ts b/packages/e2e-test-app-fabric/test/LegacyTextInputTest.test.ts index ee1205ca28f..da49f5c3b10 100644 --- a/packages/e2e-test-app-fabric/test/LegacyTextInputTest.test.ts +++ b/packages/e2e-test-app-fabric/test/LegacyTextInputTest.test.ts @@ -98,38 +98,6 @@ describe('LegacyTextInputTest', () => { ]); */ }); - - // Additional functional tests for event handlers - test('TextInput should trigger action upon onBlur', async () => { - const textInput = await textInputField(); - await textInput.click(); // Focus - - const multiLineTextInput = await multiLineTextInputField(); - await multiLineTextInput.click(); // This should trigger onBlur on the first TextInput - - await assertLogContains('onBlur'); - }); - - test('TextInput should trigger action upon onFocus', async () => { - const textInput = await textInputField(); - await textInput.click(); // This should trigger onFocus - - await assertLogContains('onFocus'); - }); - - test('TextInput should trigger action upon onChange', async () => { - const textInput = await textInputField(); - await textInput.setValue('test'); - - await assertLogContains('onChange text: test'); - }); - - test('TextInput should trigger action upon onSelectionChange', async () => { - const textInput = await textInputField(); - await textInput.setValue('test'); - - await assertLogContains('onSelectionChange range: 4,4'); - }); }); async function textInputField() { diff --git a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts index f88bee02022..1c5ab366a93 100644 --- a/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts +++ b/packages/e2e-test-app-fabric/test/TextInputComponentTest.test.ts @@ -954,7 +954,6 @@ describe('TextInput Tests', () => { expect(dump).toMatchSnapshot(); }); - // Additional functional tests for specific task requirements test('TextInput should not be editable when editable set to false', async () => { const component = await app.findElementByTestID('textinput-not-editable2'); await component.waitForDisplayed({timeout: 5000}); @@ -1160,8 +1159,8 @@ describe('TextInput Tests', () => { let dump = await dumpVisualTree('uncontrolled-textinput'); expect(dump).toMatchSnapshot(); - // Simulate focus by clicking - await component.click(); + // Call focus() method directly + await (component as any).focus(); // After focus, the style should change (component has onFocus handler) dump = await dumpVisualTree('uncontrolled-textinput'); @@ -1176,16 +1175,12 @@ describe('TextInput Tests', () => { // Focus first await component.click(); - // Then blur by clicking somewhere else - const search = await app.findElementByTestID('example_search'); - await search.click(); + // Call blur() method directly + await (component as any).blur(); // After blur, the style should revert (component has onBlur handler) const dump = await dumpVisualTree('uncontrolled-textinput'); expect(dump).toMatchSnapshot(); - - // Reset search - await search.setValue(''); }); test('TextInput isFocused() should return true when the TextInput is focused', async () => { @@ -1196,10 +1191,6 @@ describe('TextInput Tests', () => { // Focus the component await component.click(); - // Check if the component shows focused state (style change indicates focus) - const dump = await dumpVisualTree('uncontrolled-textinput'); - expect(dump).toMatchSnapshot(); - // The component should be in focused state (we can't directly test isFocused() // but we can verify the visual state that indicates focus) expect(await component.isFocused()).toBe(true); @@ -1214,10 +1205,6 @@ describe('TextInput Tests', () => { const search = await app.findElementByTestID('example_search'); await search.click(); - // Check if the component shows unfocused state - const dump = await dumpVisualTree('uncontrolled-textinput'); - expect(dump).toMatchSnapshot(); - // The component should not be in focused state expect(await component.isFocused()).toBe(false);