Skip to content
Open
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
2 changes: 1 addition & 1 deletion .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ module.name_mapper='^react-native/src/fb_internal/\(.*\)$' -> '<PROJECT_ROOT>/pa
# `src/fb_internal/*` stay importable.
module.name_mapper='^react-native/\(Libraries\|src/private\|src/types\)/.*$' -> 'react-native-deep-import-is-not-part-of-the-public-api'
module.name_mapper='^@react-native/dev-middleware$' -> '<PROJECT_ROOT>/packages/dev-middleware'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\|xml\|ktx\|heic\|heif\)$' -> '<PROJECT_ROOT>/packages/react-native/Libraries/Image/RelativeImageStub'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\|xml\|ktx\|heic\|ttf\|heif\)$' -> '<PROJECT_ROOT>/packages/react-native/Libraries/Image/RelativeImageStub'

module.system.haste.module_ref_prefix=m#

Expand Down
8 changes: 8 additions & 0 deletions .github/actions/run-fantom-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ runs:
exit 1
env:
LD_LIBRARY_PATH: ${{ github.workspace }}/private/react-native-fantom/build/tester
- name: Upload failed screenshot comparisons
if: ${{ always() }}
uses: ./.github/actions/upload-artifact
with:
name: fantom-screenshot-failures
compression-level: 1
if-no-files-found: ignore
path: private/react-native-fantom/build/screenshot-failures
- name: Upload test results
if: ${{ always() }}
uses: ./.github/actions/upload-artifact
Expand Down
40 changes: 40 additions & 0 deletions flow-typed/npm/jest-reporters_v29.x.x.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/

declare module '@jest/reporters' {
declare export class DefaultReporter<
TTestResult = unknown,
TAggregatedResult = unknown,
> {
constructor(globalConfig: unknown): void;
onRunStart(aggregatedResults: unknown, options: unknown): void;
onTestResult(
test: unknown,
result: TTestResult,
aggregatedResults: TAggregatedResult,
): void | Promise<void>;
onRunComplete(
testContexts: unknown,
aggregatedResults: TAggregatedResult,
): void;
}
declare export class VerboseReporter<
TTestResult = unknown,
TAggregatedResult = unknown,
> extends DefaultReporter<TTestResult, TAggregatedResult> {}
declare export class SummaryReporter<TAggregatedResult = unknown> {
constructor(globalConfig: unknown): void;
onRunStart(aggregatedResults: unknown, options: unknown): void;
onRunComplete(
testContexts: unknown,
aggregatedResults: TAggregatedResult,
): void;
}
}
17 changes: 17 additions & 0 deletions flow-typed/npm/pngjs_v7.x.x.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* @flow strict
* @format
*/

declare module 'pngjs/lib/png' {
declare export class PNG {
constructor(options: {width: number, height: number}): void;
width: number;
height: number;
data: Buffer;
static sync: {
read(buffer: Buffer): PNG,
write(image: PNG): Buffer,
};
}
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@babel/preset-env": "^7.25.3",
"@babel/preset-flow": "^7.24.7",
"@electron/packager": "^20.0.0",
"@expo-google-fonts/noto-color-emoji": "0.4.7",
"@expo/spawn-async": "^1.7.2",
"@jest/create-cache-key-function": "^29.7.0",
"@jest/test-sequencer": "^29.7.0",
Expand All @@ -82,6 +83,7 @@
"connect": "^3.6.5",
"debug": "^4.4.0",
"deep-equal": "1.1.1",
"dejavu-fonts-ttf": "2.37.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-babel": "^5.3.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/new-app-screen/src/NewAppScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

import Links from './Links';
import RuntimeVersion from './RuntimeVersion';
import {ThemedText, useTheme} from './Theme';
import * as React from 'react';
import {
Expand Down Expand Up @@ -135,8 +136,7 @@ function getHermesLabel(): React.Node {

return (
<ThemedText color="secondary" style={styles.label}>
JS Engine: Hermes (
{global.HermesInternal.getRuntimeProperties?.()['OSS Release Version']})
JS Engine: Hermes ({RuntimeVersion.hermes})
</ThemedText>
);
}
Expand Down
16 changes: 16 additions & 0 deletions packages/new-app-screen/src/RuntimeVersion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

const RuntimeVersion: {hermes: string | void} = {
hermes:
global.HermesInternal?.getRuntimeProperties?.()?.['OSS Release Version'],
};

export default RuntimeVersion;
4 changes: 2 additions & 2 deletions packages/react-native/Libraries/Image/ImageTypes.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {HostInstance} from '../..';
import type {RootTag} from '../Types/RootTagTypes';
import type {ResolvedAssetSource} from './AssetSourceResolver';
import type {ImageProps as ImagePropsType} from './ImageProps';
import type {ImageSource} from './ImageSource';
import typeof resolveAssetSource from './resolveAssetSource';

import * as React from 'react';

Expand Down Expand Up @@ -59,7 +59,7 @@ type ImageComponentStaticsIOS = Readonly<{
/**
* @see https://reactnative.dev/docs/image#resolveassetsource
*/
resolveAssetSource(source: ImageSource): ?ImageResolvedAssetSource,
resolveAssetSource: resolveAssetSource,
}>;

type ImageComponentStaticsAndroid = Readonly<{
Expand Down
166 changes: 166 additions & 0 deletions packages/react-native/Libraries/Text/__tests__/FontScale-itest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';

import type {RootConfig} from '@react-native/fantom';
import type {HostInstance, TextInputInstance} from 'react-native';

import * as Fantom from '@react-native/fantom';
import nullthrows from 'nullthrows';
import * as React from 'react';
import {createRef} from 'react';
import {Text, TextInput, View} from 'react-native';

const cases = [
{
name: 'default font scaling',
allowFontScaling: undefined,
maxFontSizeMultiplier: undefined,
expectedMultiplier: 2,
// Known bug: TextInput keeps its cached size; this should be 2.
expectedTextInputMultiplier: 1,
},
{
name: 'disabled font scaling',
allowFontScaling: false,
maxFontSizeMultiplier: undefined,
expectedMultiplier: 1,
expectedTextInputMultiplier: 1,
},
{
name: 'capped font scaling',
allowFontScaling: undefined,
maxFontSizeMultiplier: 1.5,
expectedMultiplier: 1.5,
// Known bug: TextInput keeps its cached size; this should be 1.5.
expectedTextInputMultiplier: 1,
},
];

describe('runtime font-scale changes', () => {
for (const componentName of ['Text', 'TextInput']) {
for (const {
name,
allowFontScaling,
maxFontSizeMultiplier,
expectedMultiplier,
expectedTextInputMultiplier,
} of cases) {
it(`handles font-scale changes for mounted ${componentName} with ${name}`, () => {
const rootConfig: RootConfig = {
viewportWidth: 400,
viewportHeight: 600,
devicePixelRatio: 2,
fontScale: 1,
viewportOffsetX: 10,
viewportOffsetY: 20,
};
const parentRef = createRef<HostInstance>();
const textRef = createRef<HostInstance>();
const inputRef = createRef<TextInputInstance>();
const content = (
<View
ref={parentRef}
style={{width: 300, height: 400, alignItems: 'flex-start'}}>
{componentName === 'Text' ? (
<Text
ref={textRef}
allowFontScaling={allowFontScaling}
maxFontSizeMultiplier={maxFontSizeMultiplier}
style={{fontSize: 20}}>
text
</Text>
) : (
<TextInput
ref={inputRef}
value="text"
allowFontScaling={allowFontScaling}
maxFontSizeMultiplier={maxFontSizeMultiplier}
style={{fontSize: 20, padding: 0}}
/>
)}
</View>
);
const expectedFontScale =
componentName === 'Text'
? expectedMultiplier
: expectedTextInputMultiplier;

// Mount and destroy the reference before creating the subject so global
// Dimensions changes cannot rerender it and invalidate its cached layout.
const referenceRoot = Fantom.createRoot({
...rootConfig,
fontScale: expectedFontScale,
});
let expectedBounds;
try {
Fantom.runTask(() => referenceRoot.render(content));
expectedBounds = nullthrows(
componentName === 'Text' ? textRef.current : inputRef.current,
).getBoundingClientRect();
} finally {
Fantom.runTask(() => referenceRoot.render(<></>));
referenceRoot.destroy();
}

const root = Fantom.createRoot(rootConfig);
try {
Fantom.runTask(() => root.render(content));

const parent = nullthrows(parentRef.current);
const text = textRef.current;
const input = inputRef.current;
const element = nullthrows(componentName === 'Text' ? text : input);
const parentBounds = parent.getBoundingClientRect();
const initialBounds = element.getBoundingClientRect();
expect(initialBounds.width).toBeGreaterThan(0);
expect(initialBounds.height).toBeGreaterThan(0);
if (expectedFontScale > 1) {
expect(expectedBounds.width).toBeGreaterThan(initialBounds.width);
expect(expectedBounds.height).toBeGreaterThan(initialBounds.height);
}
root.takeMountingManagerLogs();

root.updateSurfaceConstraints({fontScale: 2});

expect(parentRef.current).toBe(parent);
expect(parent.getBoundingClientRect()).toEqual(parentBounds);
expect(textRef.current).toBe(text);
expect(inputRef.current).toBe(input);
const scaledBounds = element.getBoundingClientRect();
expect(scaledBounds.width).toBeCloseTo(expectedBounds.width);
expect(scaledBounds.height).toBeCloseTo(expectedBounds.height);
expect(
root
.takeMountingManagerLogs()
.filter(log => /^(Create|Delete|Insert|Remove) /.test(log)),
).toEqual([]);

root.updateSurfaceConstraints({fontScale: 1});

expect(parentRef.current).toBe(parent);
expect(parent.getBoundingClientRect()).toEqual(parentBounds);
expect(textRef.current).toBe(text);
expect(inputRef.current).toBe(input);
expect(element.getBoundingClientRect()).toEqual(initialBounds);
expect(
root
.takeMountingManagerLogs()
.filter(log => /^(Create|Delete|Insert|Remove) /.test(log)),
).toEqual([]);
} finally {
Fantom.runTask(() => root.render(<></>));
root.destroy();
}
});
}
}
});
Loading
Loading