From f79ee521dbf4b4fd0a6b0356980915d4787104dd Mon Sep 17 00:00:00 2001 From: Collin Schneide <27441618+FaithfulAudio@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:04:52 -0500 Subject: [PATCH 1/7] fix(scrollview): honor programmatic scrollTo when scrollEnabled={false} (#16336) scrollEnabled={false} must only disable user scroll gestures, matching iOS and Android where setContentOffset / scrollToOffset still work when scrolling is disabled. The scrollTo command (and scrollToIndex / scrollToOffset, which route through it) previously hit a scrollEnabled early-return and was silently dropped. User-gesture input is gated separately via m_scrollVisual.ScrollEnabled (set from scrollEnabled in updateProps), so honoring a programmatic scroll here does not re-enable user scrolling. main-branch twin of #16304 (0.83-stable). --- ...t-native-windows-scrollto-scrollenabled-main.json | 1 + .../Fabric/Composition/ScrollViewComponentView.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 change/react-native-windows-scrollto-scrollenabled-main.json diff --git a/change/react-native-windows-scrollto-scrollenabled-main.json b/change/react-native-windows-scrollto-scrollenabled-main.json new file mode 100644 index 00000000000..c47de85b716 --- /dev/null +++ b/change/react-native-windows-scrollto-scrollenabled-main.json @@ -0,0 +1 @@ +{"type":"prerelease","dependentChangeType":"patch","email":"collindanielschneide@gmail.com","packageName":"react-native-windows","comment":"Honor programmatic scrollTo when scrollEnabled={false}, matching iOS/Android"} diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp index 2c58e04c42b..17d5e490749 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp @@ -1192,10 +1192,14 @@ void ScrollViewComponentView::HandleCommand(const winrt::Microsoft::ReactNative: } void ScrollViewComponentView::scrollTo(winrt::Windows::Foundation::Numerics::float3 offset, bool animate) noexcept { - if (!std::static_pointer_cast(viewProps())->scrollEnabled) { - return; - } - + // scrollEnabled={false} must only disable *user* scroll gestures, matching + // iOS and Android where setContentOffset / scrollToOffset still work when + // scrolling is disabled. Programmatic scrolls - the scrollTo command, and + // scrollToIndex / scrollToOffset which route through it - previously hit a + // scrollEnabled early-return here and were silently dropped. User-gesture + // input is gated separately (m_scrollVisual.ScrollEnabled, set from + // scrollEnabled in updateProps), so it is safe to always honor a + // programmatic scroll here. m_scrollVisual.TryUpdatePosition(offset, animate); } From ceb9e3b0561e618dbf784d78b51f8637f70f137d Mon Sep 17 00:00:00 2001 From: Collin Schneide <27441618+FaithfulAudio@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:08:36 -0500 Subject: [PATCH 2/7] fix(textinput): correct placeholder layout constraints (px vs DIP) and no-op NaN fontSize guard (#16317) * fix(textinput): correct placeholder layout constraints (px vs DIP) and no-op NaN fontSize guard Forward-port of #16303 (0.83-stable) to main. CreatePlaceholderLayout fed m_imgWidth/m_imgHeight - which are physical pixels (frame * pointScaleFactor) - into LayoutConstraints, which are expressed in DIPs. The placeholder was laid out in a box pointScaleFactor times too large, so it measured and positioned at a different height than the typed text. Divide by pointScaleFactor. The NaN fontSize guard was also a no-op: it evaluated defaultTextAttributes().fontSize as a discarded expression statement instead of assigning it, so a placeholder with no fontSize never picked up the default. * add beachball change file * Update release type to prerelease Change type from 'patch' to 'prerelease' for react-native-windows. --------- Co-authored-by: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> --- ...-native-windows-fix-textinput-placeholder-main.json | 7 +++++++ .../TextInput/WindowsTextInputComponentView.cpp | 10 +++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 change/react-native-windows-fix-textinput-placeholder-main.json diff --git a/change/react-native-windows-fix-textinput-placeholder-main.json b/change/react-native-windows-fix-textinput-placeholder-main.json new file mode 100644 index 00000000000..00cc2899b16 --- /dev/null +++ b/change/react-native-windows-fix-textinput-placeholder-main.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Fix placeholder layout constraints fed physical px instead of DIPs; fix no-op NaN fontSize guard in CreatePlaceholderLayout", + "packageName": "react-native-windows", + "email": "collindanielschneide@gmail.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp index 91ae1551daf..a8af925e960 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp @@ -1743,7 +1743,7 @@ winrt::com_ptr<::IDWriteTextLayout> WindowsTextInputComponentView::CreatePlaceho const auto &props = windowsTextInputProps(); facebook::react::TextAttributes textAttributes = props.textAttributes; if (std::isnan(props.textAttributes.fontSize)) { - facebook::react::TextAttributes::defaultTextAttributes().fontSize; + textAttributes.fontSize = facebook::react::TextAttributes::defaultTextAttributes().fontSize; } textAttributes.fontSizeMultiplier = m_fontSizeMultiplier; fragment1.string = props.placeholder; @@ -1751,8 +1751,12 @@ winrt::com_ptr<::IDWriteTextLayout> WindowsTextInputComponentView::CreatePlaceho attributedString.appendFragment(std::move(fragment1)); facebook::react::LayoutConstraints constraints; - constraints.maximumSize.width = static_cast(m_imgWidth); - constraints.maximumSize.height = static_cast(m_imgHeight); + // m_imgWidth/m_imgHeight are physical pixels (frame * pointScaleFactor), but + // LayoutConstraints are expressed in DIPs. Feeding physical px laid the + // placeholder out in a box pointScaleFactor x too large, so the placeholder was + // measured/positioned at a different height than the typed text. Convert to DIPs. + constraints.maximumSize.width = static_cast(m_imgWidth) / m_layoutMetrics.pointScaleFactor; + constraints.maximumSize.height = static_cast(m_imgHeight) / m_layoutMetrics.pointScaleFactor; facebook::react::WindowsTextLayoutManager::GetTextLayout( facebook::react::AttributedStringBox(attributedString), {} /*TODO*/, constraints, textLayout); From c716c2bc39599478d4f2f29ad78be17d75f1f084 Mon Sep 17 00:00:00 2001 From: Collin Schneide <27441618+FaithfulAudio@users.noreply.github.com> Date: Mon, 10 Aug 2026 13:55:28 -0500 Subject: [PATCH 3/7] fix(pointer): label a touch/pen contact as the primary button (#16338) onPointerPressed maps ActiveTouch.button exclusively from PointerUpdateKind, a mouse-only concept. A touch or pen contact matches no case, falls through to default: button = -1, and the derived W3C buttons bitmask becomes 0. The pointerdown delivered to JS therefore claims no button is pressed, so pointer-event-driven press handling discards finger contacts while identical mouse clicks work. Per W3C pointer-events a touch/pen contact IS the primary button: button 0, buttons 1. Set that after the switch when the mouse mapping left it negative. main counterpart of the button-labeling change in #16333 (0.83-stable), tracking #16332. Ports only that change: main already covers the tag == -1 release leak and the stale-pointer-reuse leak via #16048 (dispatching a synthesized touch Cancel), and cancels capture loss per pointer. #16333's cancel-all loop, IsPrimary purge and stale-touch backstop are deliberately not ported - they do not exist on main and their necessity there has not been assessed. --- ...react-native-windows-touch-primary-button-main.json | 7 +++++++ .../Fabric/Composition/CompositionEventHandler.cpp | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 change/react-native-windows-touch-primary-button-main.json diff --git a/change/react-native-windows-touch-primary-button-main.json b/change/react-native-windows-touch-primary-button-main.json new file mode 100644 index 00000000000..b8567189afb --- /dev/null +++ b/change/react-native-windows-touch-primary-button-main.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Fix(fabric): dispatch touch/pen contacts with the W3C primary button (button 0, buttons 1) instead of button -1 / buttons 0, so pointer-event-driven press handling responds to finger taps the same as mouse left-clicks", + "packageName": "react-native-windows", + "email": "collindanielschneide@gmail.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp index e3ee83e10ae..55eeb1f9c18 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp @@ -1419,6 +1419,16 @@ void CompositionEventHandler::onPointerPressed( break; } + // A touch (or pen) contact has no mouse PointerUpdateKind, so it fell + // through to button = -1 — and the derived W3C buttons bitmask became 0. + // The dispatched pointerdown therefore told JS "no button is pressed", so + // press machinery driven by pointer events ignored finger taps while + // identical mouse clicks (button 0 / buttons 1) worked. Per W3C + // pointer-event semantics a touch/pen contact IS the primary button. + if (pointerPoint.PointerDeviceType() != Composition::Input::PointerDeviceType::Mouse && activeTouch.button < 0) { + activeTouch.button = 0; + } + while (targetComponentView) { if (auto eventEmitter = winrt::get_self(targetComponentView) From b6cedb6056593d80a8fe01954612398780dbe09c Mon Sep 17 00:00:00 2001 From: Collin Schneide <27441618+FaithfulAudio@users.noreply.github.com> Date: Mon, 10 Aug 2026 13:56:03 -0500 Subject: [PATCH 4/7] fix(pointer): null-check the capturing component view before notifying OnPointerCaptureLost (#16337) CapturePointer and releasePointerCapture look the capturing component up by its cached m_pointerCapturingComponentTag and dereference the result unguarded. That tag can outlive the component it names: when list/ScrollView virtualization recycles the capturing row mid-pan, componentViewDescriptorWithTag returns a descriptor whose .view is null, so winrt::get_self(...)->OnPointerCaptureLost() dereferences null and terminates the process with 0xc0000005. Null-check targetComponentView at both sites. Skipping the notify loses no state transition: CapturePointer overwrites the stale tag immediately below, and releasePointerCapture clears it via the existing m_capturedPointers.empty() branch. main twin of #16334 (0.83-stable). --- ...ative-windows-capture-null-guard-main.json | 1 + .../Composition/CompositionEventHandler.cpp | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 change/react-native-windows-capture-null-guard-main.json diff --git a/change/react-native-windows-capture-null-guard-main.json b/change/react-native-windows-capture-null-guard-main.json new file mode 100644 index 00000000000..dd867f51aa1 --- /dev/null +++ b/change/react-native-windows-capture-null-guard-main.json @@ -0,0 +1 @@ +{"type":"prerelease","dependentChangeType":"patch","email":"collindanielschneide@gmail.com","packageName":"react-native-windows","comment":"Null-check the capturing component view before notifying OnPointerCaptureLost (crash when the capturing component was unmounted)"} diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp index 55eeb1f9c18..1a9e8cbc8df 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp @@ -1526,8 +1526,16 @@ bool CompositionEventHandler::CapturePointer( auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_pointerCapturingComponentTag).view; - winrt::get_self(targetComponentView) - ->OnPointerCaptureLost(); + // Guard against a stale capturing tag. If the previously-capturing component + // was unmounted (e.g. list/ScrollView virtualization recycled it during a pan) + // without releasing capture, componentViewDescriptorWithTag returns a + // descriptor whose .view is null - and the unguarded get_self(...) call then + // dereferences null and crashes the process (0xc0000005). Skip the notify when + // the view is gone; the tag is overwritten just below. + if (targetComponentView) { + winrt::get_self(targetComponentView) + ->OnPointerCaptureLost(); + } } } @@ -1556,8 +1564,13 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_pointerCapturingComponentTag).view; - winrt::get_self(targetComponentView) - ->OnPointerCaptureLost(); + // Same stale-tag null-view guard as CapturePointer above: a pointer release + // after the capturing component was unmounted would otherwise dereference a + // null view and crash (0xc0000005). + if (targetComponentView) { + winrt::get_self(targetComponentView) + ->OnPointerCaptureLost(); + } } if (m_capturedPointers.empty()) { From 351eaf81b5bbef6f81264690c801918f13b6e189 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Fri, 21 Aug 2026 13:40:51 -0700 Subject: [PATCH 5/7] Fix modifying outline property --- .../Fabric/Composition/BorderPrimitive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.cpp index 8c0a7cf2fed..5799b538d3d 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/BorderPrimitive.cpp @@ -713,7 +713,7 @@ BorderPrimitive::BorderPrimitive( : m_outer(&outer), m_rootVisual(rootVisual), m_ownsRootVisual(false) {} BorderPrimitive::BorderPrimitive(winrt::Microsoft::ReactNative::Composition::implementation::ComponentView &outer) - : m_outer(&outer), m_rootVisual(outer.CompositionContext().CreateSpriteVisual()) {} + : m_outer(&outer), m_rootVisual(outer.CompositionContext().CreateSpriteVisual()), m_ownsRootVisual(true) {} winrt::Microsoft::ReactNative::Composition::Experimental::IVisual BorderPrimitive::RootVisual() const noexcept { return m_rootVisual; From 6a040c85a23454b525d64516c8f854dd86fd210f Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Fri, 21 Aug 2026 14:41:07 -0700 Subject: [PATCH 6/7] delete createRnwApp tests in 0.81 branch --- .../cli/src/e2etest/createRnwApp.test.ts | 443 ------------------ 1 file changed, 443 deletions(-) delete mode 100644 packages/@react-native-windows/cli/src/e2etest/createRnwApp.test.ts diff --git a/packages/@react-native-windows/cli/src/e2etest/createRnwApp.test.ts b/packages/@react-native-windows/cli/src/e2etest/createRnwApp.test.ts deleted file mode 100644 index 8c4b688e0d8..00000000000 --- a/packages/@react-native-windows/cli/src/e2etest/createRnwApp.test.ts +++ /dev/null @@ -1,443 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - * @format - */ - -import fs from '@react-native-windows/fs'; -import * as path from 'path'; -import {execSync} from 'child_process'; - -/** - * Get latest stable version from npm - */ -function getLatestStableVersion(): string { - try { - return execSync('npm view react-native-windows version', { - encoding: 'utf8', - }).trim(); - } catch (error) { - throw new Error(`Could not fetch latest stable version from npm: ${error}`); - } -} - -/** - * Get latest preview version from npm - */ -function getLatestPreviewVersion(): string | undefined { - try { - const versions = JSON.parse( - execSync('npm view react-native-windows versions --json', { - encoding: 'utf8', - }), - ) as string[]; - // Preview versions usually have "preview" in the string - return versions.reverse().find(v => v.includes('preview')); - } catch (error) { - console.warn('Could not fetch preview versions from npm:', error); - return undefined; - } -} - -const LATEST_STABLE_VERSION = getLatestStableVersion(); -const LATEST_PREVIEW_VERSION = getLatestPreviewVersion(); - -// Ensure we have valid versions for testing -if (!LATEST_STABLE_VERSION) { - throw new Error('Could not fetch latest stable version from npm'); -} -if (!LATEST_PREVIEW_VERSION) { - throw new Error('Could not fetch latest preview version from npm'); -} - -/** - * Mock NPM registry response for version check - */ -const mockNpmShow = (packageNameWithVersion: string, version: string) => { - if (packageNameWithVersion === `react-native-windows@${version}`) { - const rnVersion = `^${version.split('-')[0]}`; - return { - version: version, - devDependencies: { - 'react-native': rnVersion, - }, - dependencies: { - '@react-native-community/cli': '^17.0.0', - }, - }; - } - return null; -}; - -// Test suites for stable and preview versions -describe('creaternwapp Configuration Tests - Stable Version', () => { - const repoRoot = path.resolve(__dirname, '../../../../..'); - const createRnwAppScript = path.join( - repoRoot, - 'vnext/Scripts/creaternwapp.cmd', - ); - const RNW_VERSION = LATEST_STABLE_VERSION; - - beforeAll(() => { - // Verify the script exists in the repository - expect(fs.existsSync(createRnwAppScript)).toBe(true); - - // Verify we have a stable version to test - expect(RNW_VERSION).toBeTruthy(); - expect(RNW_VERSION).not.toContain('preview'); - }); - - describe('Script Configuration Validation', () => { - test('creaternwapp.cmd script should exist and be readable', () => { - expect(fs.existsSync(createRnwAppScript)).toBe(true); - - const scriptContent = fs.readFileSync(createRnwAppScript, 'utf8'); - - // Verify script contains expected parameters - expect(scriptContent).toContain('/rnw'); - expect(scriptContent).toContain('/t'); - expect(scriptContent).toContain('/verdaccio'); - expect(scriptContent).toContain('cpp-app'); - }); - - test('should validate fabric template is the default', () => { - const scriptContent = fs.readFileSync(createRnwAppScript, 'utf8'); - - // Default template should be cpp-app (fabric/new arch) - expect(scriptContent).toContain('set RNW_TEMPLATE_TYPE=cpp-app'); - }); - - test('should validate old UWP templates are available', () => { - const oldTemplatesDir = path.join(repoRoot, 'vnext/templates/old'); - expect(fs.existsSync(oldTemplatesDir)).toBe(true); - - const uwpCppAppTemplate = path.join(oldTemplatesDir, 'uwp-cpp-app'); - expect(fs.existsSync(uwpCppAppTemplate)).toBe(true); - }); - - test('should validate new fabric templates are available', () => { - const newTemplatesDir = path.join(repoRoot, 'vnext/template'); - expect(fs.existsSync(newTemplatesDir)).toBe(true); - - const cppAppTemplate = path.join(newTemplatesDir, 'cpp-app'); - expect(fs.existsSync(cppAppTemplate)).toBe(true); - }); - }); - - describe('Version Configuration Tests', () => { - test('should validate target version is available', async () => { - // This test validates that the specified version exists in npm - // In a real environment, this would use npm show to check the version - expect(RNW_VERSION).toBeTruthy(); - - // Mock version validation - in actual Windows CI this would call npm show - const versionInfo = mockNpmShow( - `react-native-windows@${RNW_VERSION}`, - RNW_VERSION, - ); - expect(versionInfo).toBeTruthy(); - expect(versionInfo?.version).toBe(RNW_VERSION); - }); - - test('should validate required dependencies for target version', () => { - const versionInfo = mockNpmShow( - `react-native-windows@${RNW_VERSION}`, - RNW_VERSION, - ); - - // Validate that the version has the expected dependency structure - expect(versionInfo?.devDependencies).toHaveProperty('react-native'); - expect(versionInfo?.dependencies).toHaveProperty( - '@react-native-community/cli', - ); - }); - }); - - describe('Template Configuration Tests', () => { - test('should validate new architecture (fabric) command structure', () => { - // Test the command that would be used for new arch: - // creaternwapp.cmd /rnw preview test-app-name - const expectedCommand = `creaternwapp.cmd /rnw preview test-app-name`; - - expect(expectedCommand).toContain('/rnw preview'); - expect(expectedCommand).toContain('test-app-name'); - expect(expectedCommand).not.toContain('/t'); // No template specified = default fabric - }); - - test('should validate old architecture (paper/UWP) command structure', () => { - // Test the command that would be used for old arch: - // creaternwapp.cmd /rnw preview /t old/uwp-cpp-app test-app-name - const expectedCommand = `creaternwapp.cmd /rnw preview /t old/uwp-cpp-app test-app-name`; - - expect(expectedCommand).toContain('/rnw preview'); - expect(expectedCommand).toContain('/t old/uwp-cpp-app'); - expect(expectedCommand).toContain('test-app-name'); - }); - - test('should validate yarn windows command variations', () => { - // Test the commands that would be used to build apps: - const debugCommand = 'yarn windows'; - const releaseCommand = 'yarn windows --release'; - - expect(debugCommand).toBe('yarn windows'); - expect(releaseCommand).toBe('yarn windows --release'); - expect(releaseCommand).toContain('--release'); - }); - }); - - describe('Workflow Integration Tests', () => { - test('should validate complete new architecture workflow commands', () => { - const workflow = { - create: `creaternwapp.cmd /rnw ${RNW_VERSION} TestAppFabric`, - start: 'yarn start', - buildDebug: 'yarn windows', - buildRelease: 'yarn windows --release', - }; - - expect(workflow.create).toContain(RNW_VERSION); - expect(workflow.create).toContain('TestAppFabric'); - expect(workflow.start).toBe('yarn start'); - expect(workflow.buildDebug).toBe('yarn windows'); - expect(workflow.buildRelease).toBe('yarn windows --release'); - }); - - test('should validate complete old architecture workflow commands', () => { - const workflow = { - create: `creaternwapp.cmd /rnw ${RNW_VERSION} /t old/uwp-cpp-app TestAppPaper`, - start: 'yarn start', - buildDebug: 'yarn windows', - buildRelease: 'yarn windows --release', - }; - - expect(workflow.create).toContain(RNW_VERSION); - expect(workflow.create).toContain('/t old/uwp-cpp-app'); - expect(workflow.create).toContain('TestAppPaper'); - expect(workflow.start).toBe('yarn start'); - expect(workflow.buildDebug).toBe('yarn windows'); - expect(workflow.buildRelease).toBe('yarn windows --release'); - }); - }); - - describe('Documentation and Example Validation', () => { - test('should document the testing procedure for new architecture', () => { - const procedure = { - title: 'New Architecture (Fabric) Testing', - steps: [ - `creaternwapp.cmd /rnw ${RNW_VERSION} test-app-name`, - 'yarn start (in test-app-name path)', - 'cd test-app-name && yarn windows', - 'cd test-app-name && yarn windows --release', - ], - }; - - expect(procedure.steps[0]).toContain(RNW_VERSION); - expect(procedure.steps[1]).toContain('yarn start'); - expect(procedure.steps[2]).toContain('yarn windows'); - expect(procedure.steps[3]).toContain('yarn windows --release'); - }); - - test('should document the testing procedure for old architecture', () => { - const procedure = { - title: 'Old Architecture (Paper/UWP) Testing', - steps: [ - `creaternwapp.cmd /rnw ${RNW_VERSION} /t old/uwp-cpp-app test-app-name`, - 'yarn start (in test-app-name path)', - 'cd test-app-name && yarn windows', - 'cd test-app-name && yarn windows --release', - ], - }; - - expect(procedure.steps[0]).toContain(RNW_VERSION); - expect(procedure.steps[0]).toContain('/t old/uwp-cpp-app'); - expect(procedure.steps[1]).toContain('yarn start'); - expect(procedure.steps[2]).toContain('yarn windows'); - expect(procedure.steps[3]).toContain('yarn windows --release'); - }); - }); -}); - -describe('creaternwapp Configuration Tests - Preview Version', () => { - const repoRoot = path.resolve(__dirname, '../../../../..'); - const createRnwAppScript = path.join( - repoRoot, - 'vnext/Scripts/creaternwapp.cmd', - ); - const RNW_VERSION = LATEST_PREVIEW_VERSION; - - beforeAll(() => { - // Verify the script exists in the repository - expect(fs.existsSync(createRnwAppScript)).toBe(true); - - // Verify we have a preview version to test - expect(RNW_VERSION).toBeTruthy(); - expect(RNW_VERSION).toContain('preview'); - }); - - describe('Script Configuration Validation', () => { - test('creaternwapp.cmd script should exist and be readable', () => { - expect(fs.existsSync(createRnwAppScript)).toBe(true); - - const scriptContent = fs.readFileSync(createRnwAppScript, 'utf8'); - - // Verify script contains expected parameters - expect(scriptContent).toContain('/rnw'); - expect(scriptContent).toContain('/t'); - expect(scriptContent).toContain('/verdaccio'); - expect(scriptContent).toContain('cpp-app'); - }); - - test('should validate fabric template is the default', () => { - const scriptContent = fs.readFileSync(createRnwAppScript, 'utf8'); - - // Default template should be cpp-app (fabric/new arch) - expect(scriptContent).toContain('set RNW_TEMPLATE_TYPE=cpp-app'); - }); - - test('should validate old UWP templates are available', () => { - const oldTemplatesDir = path.join(repoRoot, 'vnext/templates/old'); - expect(fs.existsSync(oldTemplatesDir)).toBe(true); - - const uwpCppAppTemplate = path.join(oldTemplatesDir, 'uwp-cpp-app'); - expect(fs.existsSync(uwpCppAppTemplate)).toBe(true); - }); - - test('should validate new fabric templates are available', () => { - const newTemplatesDir = path.join(repoRoot, 'vnext/template'); - expect(fs.existsSync(newTemplatesDir)).toBe(true); - - const cppAppTemplate = path.join(newTemplatesDir, 'cpp-app'); - expect(fs.existsSync(cppAppTemplate)).toBe(true); - }); - }); - - describe('Version Configuration Tests', () => { - test('should validate target version is available', async () => { - // This test validates that the specified version exists in npm - // In a real environment, this would use npm show to check the version - expect(RNW_VERSION).toBeTruthy(); - - // Mock version validation - in actual Windows CI this would call npm show - const versionInfo = mockNpmShow( - `react-native-windows@${RNW_VERSION}`, - RNW_VERSION, - ); - expect(versionInfo).toBeTruthy(); - expect(versionInfo?.version).toBe(RNW_VERSION); - }); - - test('should validate required dependencies for target version', () => { - const versionInfo = mockNpmShow( - `react-native-windows@${RNW_VERSION}`, - RNW_VERSION, - ); - - // Validate that the version has the expected dependency structure - expect(versionInfo?.devDependencies).toHaveProperty('react-native'); - expect(versionInfo?.dependencies).toHaveProperty( - '@react-native-community/cli', - ); - }); - }); - - describe('Template Configuration Tests', () => { - test('should validate new architecture (fabric) command structure', () => { - // Test the command that would be used for new arch: - // creaternwapp.cmd /rnw preview test-app-name - const expectedCommand = `creaternwapp.cmd /rnw preview test-app-name`; - - expect(expectedCommand).toContain('/rnw preview'); - expect(expectedCommand).toContain('test-app-name'); - expect(expectedCommand).not.toContain('/t'); // No template specified = default fabric - }); - - test('should validate old architecture (paper/UWP) command structure', () => { - // Test the command that would be used for old arch: - // creaternwapp.cmd /rnw preview /t old/uwp-cpp-app test-app-name - const expectedCommand = `creaternwapp.cmd /rnw preview /t old/uwp-cpp-app test-app-name`; - - expect(expectedCommand).toContain('/rnw preview'); - expect(expectedCommand).toContain('/t old/uwp-cpp-app'); - expect(expectedCommand).toContain('test-app-name'); - }); - - test('should validate yarn windows command variations', () => { - // Test the commands that would be used to build apps: - const debugCommand = 'yarn windows'; - const releaseCommand = 'yarn windows --release'; - - expect(debugCommand).toBe('yarn windows'); - expect(releaseCommand).toBe('yarn windows --release'); - expect(releaseCommand).toContain('--release'); - }); - }); - - describe('Workflow Integration Tests', () => { - test('should validate complete new architecture workflow commands', () => { - const workflow = { - create: `creaternwapp.cmd /rnw ${RNW_VERSION} TestAppFabric`, - start: 'yarn start', - buildDebug: 'yarn windows', - buildRelease: 'yarn windows --release', - }; - - expect(workflow.create).toContain(RNW_VERSION); - expect(workflow.create).toContain('TestAppFabric'); - expect(workflow.start).toBe('yarn start'); - expect(workflow.buildDebug).toBe('yarn windows'); - expect(workflow.buildRelease).toBe('yarn windows --release'); - }); - - test('should validate complete old architecture workflow commands', () => { - const workflow = { - create: `creaternwapp.cmd /rnw ${RNW_VERSION} /t old/uwp-cpp-app TestAppPaper`, - start: 'yarn start', - buildDebug: 'yarn windows', - buildRelease: 'yarn windows --release', - }; - - expect(workflow.create).toContain(RNW_VERSION); - expect(workflow.create).toContain('/t old/uwp-cpp-app'); - expect(workflow.create).toContain('TestAppPaper'); - expect(workflow.start).toBe('yarn start'); - expect(workflow.buildDebug).toBe('yarn windows'); - expect(workflow.buildRelease).toBe('yarn windows --release'); - }); - }); - - describe('Documentation and Example Validation', () => { - test('should document the testing procedure for new architecture', () => { - const procedure = { - title: 'New Architecture (Fabric) Testing', - steps: [ - `creaternwapp.cmd /rnw ${RNW_VERSION} test-app-name`, - 'yarn start (in test-app-name path)', - 'cd test-app-name && yarn windows', - 'cd test-app-name && yarn windows --release', - ], - }; - - expect(procedure.steps[0]).toContain(RNW_VERSION); - expect(procedure.steps[1]).toContain('yarn start'); - expect(procedure.steps[2]).toContain('yarn windows'); - expect(procedure.steps[3]).toContain('yarn windows --release'); - }); - - test('should document the testing procedure for old architecture', () => { - const procedure = { - title: 'Old Architecture (Paper/UWP) Testing', - steps: [ - `creaternwapp.cmd /rnw ${RNW_VERSION} /t old/uwp-cpp-app test-app-name`, - 'yarn start (in test-app-name path)', - 'cd test-app-name && yarn windows', - 'cd test-app-name && yarn windows --release', - ], - }; - - expect(procedure.steps[0]).toContain(RNW_VERSION); - expect(procedure.steps[0]).toContain('/t old/uwp-cpp-app'); - expect(procedure.steps[1]).toContain('yarn start'); - expect(procedure.steps[2]).toContain('yarn windows'); - expect(procedure.steps[3]).toContain('yarn windows --release'); - }); - }); -}); From 0669ebd0e14a9010bb26d97e73ca71b5cda91113 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Fri, 21 Aug 2026 15:03:22 -0700 Subject: [PATCH 7/7] Change files --- ...e-windows-cli-60d1d9e9-e95e-4f94-8cc5-37e48aa5c1a3.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@react-native-windows-cli-60d1d9e9-e95e-4f94-8cc5-37e48aa5c1a3.json diff --git a/change/@react-native-windows-cli-60d1d9e9-e95e-4f94-8cc5-37e48aa5c1a3.json b/change/@react-native-windows-cli-60d1d9e9-e95e-4f94-8cc5-37e48aa5c1a3.json new file mode 100644 index 00000000000..b562e515004 --- /dev/null +++ b/change/@react-native-windows-cli-60d1d9e9-e95e-4f94-8cc5-37e48aa5c1a3.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "delete createRnwApp tests in 0.81 branch", + "packageName": "@react-native-windows/cli", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "none" +}