Skip to content

Commit c447d57

Browse files
authored
feat: out-of-bounds screenshots (#64)
This pull request updates the screenshot-capturing logic to allow capturing out-of-bounds views along with their children. The new logic is enabled only when views actually extend out of bounds, as it has a limitation: view flattening must be turned off for it to work correctly. To address this, a new configuration option has been added to disable flattening globally.
1 parent a0c5310 commit c447d57

36 files changed

Lines changed: 740 additions & 174 deletions

apps/playground/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ PODS:
55
- FBLazyVector (0.82.1)
66
- fmt (11.0.2)
77
- glog (0.3.5)
8-
- HarnessUI (1.0.0-alpha.20):
8+
- HarnessUI (1.0.0-alpha.24):
99
- boost
1010
- DoubleConversion
1111
- fast_float
@@ -2592,7 +2592,7 @@ SPEC CHECKSUMS:
25922592
FBLazyVector: 0aa6183b9afe3c31fc65b5d1eeef1f3c19b63bfa
25932593
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
25942594
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
2595-
HarnessUI: 2957b94c9c4a7e6e54b636229f4aa5e3809936bf
2595+
HarnessUI: 9593f42f9c8f68200ccd07a6ed64d02de42637b1
25962596
hermes-engine: 273e30e7fb618279934b0b95ffab60ecedb7acf5
25972597
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
25982598
RCTDeprecation: f17e2ebc07876ca9ab8eb6e4b0a4e4647497ae3a

apps/playground/rn-harness.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,5 @@ export default {
6969
resetEnvironmentBetweenTestFiles: true,
7070
unstable__skipAlreadyIncludedModules: false,
7171
forwardClientLogs: true,
72+
disableViewFlattening: true,
7273
};
3.42 KB
Loading
663 Bytes
Loading
29.4 KB
Loading
663 Bytes
Loading
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, test, render, expect } from "react-native-harness";
2+
import { View } from "react-native";
3+
import { screen } from "@react-native-harness/ui";
4+
5+
const COLORS = ['red', 'green', 'blue', 'yellow', 'purple', 'orange', 'pink', 'brown', 'gray', 'black'];
6+
7+
describe('Out of bounds', () => {
8+
test('should screenshot specific element only', async () => {
9+
await render(
10+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
11+
<View testID="out-of-bounds" style={{ width: 1000, height: 100, backgroundColor: 'yellow', flexDirection: 'row' }}>
12+
{Array.from({ length: 10 }).map((_, index) => (
13+
<View key={index} style={{ width: 100, height: 100, backgroundColor: COLORS[index % COLORS.length] }} />
14+
))}
15+
</View>
16+
</View>
17+
);
18+
19+
const element = await screen.findByTestId('out-of-bounds');
20+
const screenshot = await screen.screenshot(element);
21+
await expect(screenshot).toMatchImageSnapshot({
22+
name: 'out-of-bounds',
23+
});
24+
});
25+
});

packages/babel-preset/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"babel-plugin-istanbul": "^7.0.1"
2020
},
2121
"peerDependencies": {
22-
"@babel/core": "^7.22.0"
22+
"@babel/core": "^7.22.0",
23+
"@babel/plugin-transform-react-jsx": "*"
2324
},
2425
"devDependencies": {
2526
"@babel/core": "^7.22.0",

packages/babel-preset/src/preset.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ export const rnHarnessPlugins = [
2121
'@babel/plugin-transform-class-static-block',
2222
resolveWeakPlugin,
2323
getIstanbulPlugin(),
24+
[
25+
'@babel/plugin-transform-react-jsx',
26+
{
27+
runtime: 'automatic',
28+
importSource: '@react-native-harness/runtime',
29+
},
30+
],
2431
].filter((plugin) => plugin !== null);
2532

2633
export const rnHarnessPreset = () => {

packages/config/src/types.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ export const ConfigSchema = z
4747
.min(100, 'Crash detection interval must be at least 100ms')
4848
.default(500),
4949

50+
disableViewFlattening: z
51+
.boolean()
52+
.optional()
53+
.default(false)
54+
.describe(
55+
'Disable view flattening in React Native. This will set collapsable={true} for all View components ' +
56+
'to ensure they are not flattened by the native layout engine.'
57+
),
58+
5059
coverage: z
5160
.object({
5261
root: z
@@ -56,7 +65,7 @@ export const ConfigSchema = z
5665
'Root directory for coverage instrumentation in monorepo setups. ' +
5766
'Specifies the directory from which coverage data should be collected. ' +
5867
'Use ".." for create-react-native-library projects where tests run from example/ ' +
59-
'but source files are in parent directory. Passed to babel-plugin-istanbul\'s cwd option.'
68+
"but source files are in parent directory. Passed to babel-plugin-istanbul's cwd option."
6069
),
6170
})
6271
.optional(),

0 commit comments

Comments
 (0)