Skip to content

Add functional tests for Button component fast refresh scenarios #14772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Add functional tests for Button component fast refresh scenarios",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,146 @@ exports.examples = [
);
},
},
{
title: 'Button with dynamic text',
description: 'Button text updates when pressed',
render: function (): React.Node {
return <DynamicTextButton />;
},
},
{
title: 'Button with dynamic color',
description: 'Button color updates when pressed',
render: function (): React.Node {
return <DynamicColorButton />;
},
},
{
title: 'Button with dynamic disabled state',
description: 'Button disabled state toggles when pressed',
render: function (): React.Node {
return <DynamicDisabledButton />;
},
},
{
title: 'Button with dynamic styling on press',
description: 'Button updates styling when pressed',
render: function (): React.Node {
return <DynamicStyleButton />;
},
},
];

// Dynamic Button Components for fast refresh testing
function DynamicTextButton(): React.Node {
const [buttonText, setButtonText] = React.useState('Initial Text');
const [pressCount, setPressCount] = React.useState(0);

const onPress = () => {
const newCount = pressCount + 1;
setPressCount(newCount);
setButtonText(`Pressed ${newCount} times`);
};

return (
<Button
onPress={onPress}
testID="dynamic_text_button"
title={buttonText}
accessibilityLabel="Press to change button text"
/>
);
}

function DynamicColorButton(): React.Node {
const [colorIndex, setColorIndex] = React.useState(0);
const colors = ['#007AFF', '#FF3B30', '#34C759', '#FF9500', '#5856D6'];

const onPress = () => {
setColorIndex((prev) => (prev + 1) % colors.length);
};

return (
<RNTesterThemeContext.Consumer>
{theme => (
<Button
onPress={onPress}
testID="dynamic_color_button"
color={colors[colorIndex]}
title="Change Color"
accessibilityLabel="Press to change button color"
/>
)}
</RNTesterThemeContext.Consumer>
);
}

function DynamicDisabledButton(): React.Node {
const [isDisabled, setIsDisabled] = React.useState(false);
const [toggleText, setToggleText] = React.useState('Disable Me');

const onPress = () => {
if (!isDisabled) {
setIsDisabled(true);
setToggleText('Disabled');
// Re-enable after 2 seconds for testing
setTimeout(() => {
setIsDisabled(false);
setToggleText('Disable Me');
}, 2000);
}
};

return (
<Button
disabled={isDisabled}
onPress={onPress}
testID="dynamic_disabled_button"
title={toggleText}
accessibilityLabel="Press to toggle disabled state"
/>
);
}

function DynamicStyleButton(): React.Node {
const [isPressed, setIsPressed] = React.useState(false);
const [pressCount, setPressCount] = React.useState(0);

const onPress = () => {
setIsPressed(true);
setPressCount(prev => prev + 1);
// Reset pressed state after visual feedback
setTimeout(() => setIsPressed(false), 300);
};

return (
<RNTesterThemeContext.Consumer>
{theme => (
<View style={[styles.dynamicContainer, isPressed && styles.pressedContainer]} testID="dynamic_style_container">
<Button
onPress={onPress}
testID="dynamic_style_button"
color={isPressed ? theme.SystemRedColor : theme.LinkColor}
title={`Style Button (${pressCount})`}
accessibilityLabel="Press to update styling"
/>
</View>
)}
</RNTesterThemeContext.Consumer>
);
}

const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'space-between',
},
dynamicContainer: {
padding: 10,
borderRadius: 5,
backgroundColor: 'transparent',
},
pressedContainer: {
backgroundColor: '#f0f0f0',
},
});
95 changes: 95 additions & 0 deletions packages/e2e-test-app-fabric/test/ButtonComponentTest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,99 @@ describe('Button Tests', () => {
const dump3 = await dumpVisualTree('accessible_focusable_false_button');
expect(dump3).toMatchSnapshot();
});

// Functional tests for dynamic button behaviors
test('Button text should update on fast refresh', async () => {
await searchBox('dynamic text');
const component = await app.findElementByTestID('dynamic_text_button');
await component.waitForDisplayed({timeout: 5000});

// Get initial state
const initialDump = await dumpVisualTree('dynamic_text_button');
expect(initialDump).toMatchSnapshot('initial-text');

// Click to change text
await component.click();

// Verify text updated
const updatedDump = await dumpVisualTree('dynamic_text_button');
expect(updatedDump).toMatchSnapshot('updated-text');
expect(updatedDump.Text).toContain('Pressed 1 times');
});

test('Button color should update on fast refresh', async () => {
await searchBox('dynamic color');
const component = await app.findElementByTestID('dynamic_color_button');
await component.waitForDisplayed({timeout: 5000});

// Get initial state
const initialDump = await dumpVisualTree('dynamic_color_button');
expect(initialDump).toMatchSnapshot('initial-color');

// Click to change color
await component.click();

// Verify color updated (visual tree should show different styling)
const updatedDump = await dumpVisualTree('dynamic_color_button');
expect(updatedDump).toMatchSnapshot('updated-color');
});

test('Button disabled status should update on fast refresh', async () => {
await searchBox('dynamic disabled');
const component = await app.findElementByTestID('dynamic_disabled_button');
await component.waitForDisplayed({timeout: 5000});

// Get initial state (should be enabled)
const initialDump = await dumpVisualTree('dynamic_disabled_button');
expect(initialDump).toMatchSnapshot('initial-enabled');

// Click to disable
await component.click();

// Verify button is now disabled
const disabledDump = await dumpVisualTree('dynamic_disabled_button');
expect(disabledDump).toMatchSnapshot('disabled-state');
expect(disabledDump.Text).toContain('Disabled');

// Wait for auto re-enable (2 seconds)
await app.waitUntil(
async () => {
const dump = await dumpVisualTree('dynamic_disabled_button');
return dump.Text.includes('Disable Me');
},
{
timeout: 3000,
interval: 500,
timeoutMsg: 'Button should auto re-enable after 2 seconds',
}
);

// Verify button is enabled again
const reEnabledDump = await dumpVisualTree('dynamic_disabled_button');
expect(reEnabledDump).toMatchSnapshot('re-enabled-state');
});

test('Button should update relevant styling upon press', async () => {
await searchBox('dynamic styling');
const component = await app.findElementByTestID('dynamic_style_button');
await component.waitForDisplayed({timeout: 5000});

// Get initial state of both button and container
const initialDump = await dumpVisualTree('dynamic_style_button');
expect(initialDump).toMatchSnapshot('initial-styling');
const initialContainerDump = await dumpVisualTree('dynamic_style_container');
expect(initialContainerDump).toMatchSnapshot('initial-container-styling');

// Click to change styling
await component.click();

// Verify styling updated (should show press count and temporary color change)
const updatedDump = await dumpVisualTree('dynamic_style_button');
expect(updatedDump).toMatchSnapshot('updated-styling');
expect(updatedDump.Text).toContain('Style Button (1)');

// Also verify container styling changed
const updatedContainerDump = await dumpVisualTree('dynamic_style_container');
expect(updatedContainerDump).toMatchSnapshot('updated-container-styling');
});
});
Loading