Commit 4b3f0e2ffbb for woocommerce

commit 4b3f0e2ffbb6a5de2f682f1c0de3035709f0caa7
Author: Ahmed <ahmed.el.azzabi@automattic.com>
Date:   Thu Jul 16 13:31:37 2026 +0300

    Add CI lint and JS test config to WooCommerce Beta Tester (#66678)

    * Add CI lint and JS test config to WooCommerce Beta Tester

    The @woocommerce/plugin-woocommerce-beta-tester package.json had no
    config.ci block, so the monorepo CI job generator never created lint or
    JavaScript test jobs for it. As a result, changes to the package or its
    monorepo dependencies were not validated in CI.

    Add a config.ci block with a lint job and a JavaScript unit test job,
    following the convention used by the other JS packages in the monorepo
    (clean 'lint' / 'test:js' command names, with scoping kept inside the
    npm scripts):

    - Add a 'lint' script scoped to src (the modern React/TypeScript app),
      matching how packages/js/* lint their source. The pre-existing whole
      package 'lint:js' script is left untouched for local use and lint-staged.
    - Rename 'test:unit' to 'test:js' to match the convention used across the
      monorepo, and pass --passWithNoTests so the job succeeds until JS unit
      tests are added to the package.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SqnbfxbcsNhsyXTsXWnear

    * Fix pre-existing lint errors in Beta Tester src surfaced by the new lint job

    Enabling the lint job revealed 23 pre-existing ESLint errors in
    plugins/woocommerce-beta-tester/src that were never caught because CI
    never linted this package. Fix them so the newly added lint job passes:

    - prettier/prettier formatting (example-fills, rest-api-filters, tools/data)
    - @typescript-eslint/no-use-before-define: hoist action creators and the
      load/version helpers above their first use (features/data/actions.js,
      tools/commands/load-template-version.js)
    - @typescript-eslint/no-shadow and no-unused-vars: rename shadowed locals
      and drop the unused isError binding (payments)
    - object-shorthand: use shorthand for the data property (payments)
    - @typescript-eslint/ban-ts-comment and no-console: annotate the
      intentional @ts-ignore and console.error calls with eslint-disable

    All changes are behaviour-preserving. Remaining ESLint warnings
    (import/no-unresolved for workspace deps, @wordpress/no-unsafe-wp-apis,
    react-hooks/exhaustive-deps) are warnings and do not fail the job.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SqnbfxbcsNhsyXTsXWnear

    * Remove dead isError computation in Beta Tester payments panel

    CodeRabbit flagged that getOrdersError() was called with the orders
    result instead of the query. getOrdersError( state, query ) is keyed by
    the query (via getOrderResourceName), so passing the orders array looked
    up the wrong cache key and always yielded false.

    The computed isError value was never consumed by the component (ESLint
    originally flagged it as unused), so rather than correct an argument for
    a discarded value, remove the dead isError property and the now-unused
    getOrdersError selector entirely.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SqnbfxbcsNhsyXTsXWnear

    * Address review: correct dependency-trigger claim and drop always-false isRequesting

    Following @vladolaru's review:

    - The CI generator's dependency cascade only feeds test jobs (JobType.Test
      in job-processing.ts), not lint jobs, so the lint job does not trigger on
      monorepo dependency changes. Reword the changelog to state that only the
      JavaScript test job runs on dependency changes.
    - Remove isRequesting from the payments panel: getOrders(query, null)
      registers resolution under [query, null] while hasFinishedResolution
      probes [query], so the keys never match and isRequesting was permanently
      false. Deleting it (like the earlier isError removal) is behaviour-
      preserving, whereas a partial fix would blank the orders table. Also
      guard the tbody with orders?.length > 0 to avoid rendering a stray 0.
    - Add eslint.config.mjs to the lint job's change globs so the trigger keeps
      working after the ESLint flat-config migration (#66078) replaces
      .eslintrc/.eslintignore.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SqnbfxbcsNhsyXTsXWnear

    * Drop ineffective orders = [] default in Beta Tester payments panel

    getOrders( query, null ) returns null (not undefined) while resolving, so
    the destructuring default never applied. Remove it so the nullable loading
    state is explicit; the render already guards with orders?.length.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SqnbfxbcsNhsyXTsXWnear

    ---------

    Co-authored-by: Claude <noreply@anthropic.com>

diff --git a/plugins/woocommerce-beta-tester/changelog/add-55908-beta-tester-ci-config b/plugins/woocommerce-beta-tester/changelog/add-55908-beta-tester-ci-config
new file mode 100644
index 00000000000..ef72afe9441
--- /dev/null
+++ b/plugins/woocommerce-beta-tester/changelog/add-55908-beta-tester-ci-config
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Add CI configuration to the WooCommerce Beta Tester package.json so lint and JavaScript unit test jobs run when the package's code changes; the JavaScript test job additionally runs when a monorepo dependency changes.
diff --git a/plugins/woocommerce-beta-tester/package.json b/plugins/woocommerce-beta-tester/package.json
index 4326541071f..506aebad976 100644
--- a/plugins/woocommerce-beta-tester/package.json
+++ b/plugins/woocommerce-beta-tester/package.json
@@ -59,7 +59,34 @@
 		}
 	},
 	"config": {
-		"build_step": "pnpm run build:zip"
+		"build_step": "pnpm run build:zip",
+		"ci": {
+			"lint": {
+				"command": "lint",
+				"changes": [
+					".eslintrc",
+					".eslintignore",
+					"eslint.config.mjs",
+					"tsconfig.json",
+					"src/**/*.{js,jsx,ts,tsx}"
+				]
+			},
+			"tests": [
+				{
+					"name": "JavaScript",
+					"command": "test:js",
+					"changes": [
+						"tsconfig.json",
+						"src/**/*.{js,jsx,ts,tsx}",
+						"typing/**/*.ts"
+					],
+					"events": [
+						"pull_request",
+						"push"
+					]
+				}
+			]
+		}
 	},
 	"scripts": {
 		"build": "pnpm build:project",
@@ -73,6 +100,7 @@
 		"check-licenses": "wp-scripts check-licenses",
 		"format:js": "wp-scripts format-js",
 		"postinstall": "XDEBUG_MODE=off composer install --quiet",
+		"lint": "wp-scripts lint-js src",
 		"lint:css": "wp-scripts lint-style",
 		"lint:css:fix": "wp-scripts lint-style --fix",
 		"lint:js": "wp-scripts lint-js",
@@ -85,7 +113,7 @@
 		"packages-update": "wp-scripts packages-update",
 		"start": "wp-scripts start",
 		"test:e2e": "wp-scripts test-e2e",
-		"test:unit": "wp-scripts test-unit-js",
+		"test:js": "wp-scripts test-unit-js --passWithNoTests",
 		"uglify": "rm -f $npm_package_assets_js_min && for f in $npm_package_assets_js_js; do file=${f%.js}; node_modules/.bin/uglifyjs $f -c -m > $file.min.js; done"
 	},
 	"engines": {
diff --git a/plugins/woocommerce-beta-tester/src/example-fills/experimental-woocommerce-wcpay-feature.js b/plugins/woocommerce-beta-tester/src/example-fills/experimental-woocommerce-wcpay-feature.js
index 369a4a5101f..25bf2e2eeaf 100644
--- a/plugins/woocommerce-beta-tester/src/example-fills/experimental-woocommerce-wcpay-feature.js
+++ b/plugins/woocommerce-beta-tester/src/example-fills/experimental-woocommerce-wcpay-feature.js
@@ -14,7 +14,10 @@ const MyFill = () => (
 	</Fill>
 );

-if ( window.wcAdminFeatures && window.wcAdminFeatures[ 'beta-tester-slotfill-examples' ] ) {
+if (
+	window.wcAdminFeatures &&
+	window.wcAdminFeatures[ 'beta-tester-slotfill-examples' ]
+) {
 	registerPlugin(
 		'beta-tester-woocommerce-experiments-placeholder-slotfill-example',
 		{
diff --git a/plugins/woocommerce-beta-tester/src/features/data/actions.js b/plugins/woocommerce-beta-tester/src/features/data/actions.js
index 25bc100d791..14e0412048e 100644
--- a/plugins/woocommerce-beta-tester/src/features/data/actions.js
+++ b/plugins/woocommerce-beta-tester/src/features/data/actions.js
@@ -10,6 +10,20 @@ import { controls } from '@wordpress/data';
 import TYPES from './action-types';
 import { API_NAMESPACE, STORE_KEY } from './constants';

+export function setFeatures( features ) {
+	return {
+		type: TYPES.SET_FEATURES,
+		features,
+	};
+}
+
+export function setModifiedFeatures( modifiedFeatures ) {
+	return {
+		type: TYPES.SET_MODIFIED_FEATURES,
+		modifiedFeatures,
+	};
+}
+
 export function* resetModifiedFeatures() {
 	try {
 		const response = yield apiFetch( {
@@ -41,17 +55,3 @@ export function* toggleFeature( featureName ) {
 		throw new Error();
 	}
 }
-
-export function setFeatures( features ) {
-	return {
-		type: TYPES.SET_FEATURES,
-		features,
-	};
-}
-
-export function setModifiedFeatures( modifiedFeatures ) {
-	return {
-		type: TYPES.SET_MODIFIED_FEATURES,
-		modifiedFeatures,
-	};
-}
diff --git a/plugins/woocommerce-beta-tester/src/live-branches/App.tsx b/plugins/woocommerce-beta-tester/src/live-branches/App.tsx
index 356a6c46937..3c8d4b427f4 100644
--- a/plugins/woocommerce-beta-tester/src/live-branches/App.tsx
+++ b/plugins/woocommerce-beta-tester/src/live-branches/App.tsx
@@ -3,6 +3,7 @@
  */
 import { Spinner } from '@woocommerce/components';
 import {
+	// eslint-disable-next-line @typescript-eslint/ban-ts-comment
 	// @ts-ignore
 	__experimentalHeading as Heading,
 } from '@wordpress/components';
diff --git a/plugins/woocommerce-beta-tester/src/payments/index.js b/plugins/woocommerce-beta-tester/src/payments/index.js
index 5a7eb4e10e7..711732a4c96 100644
--- a/plugins/woocommerce-beta-tester/src/payments/index.js
+++ b/plugins/woocommerce-beta-tester/src/payments/index.js
@@ -9,25 +9,16 @@ import apiFetch from '@wordpress/api-fetch';
 const metaKey = '_wcpay_mode';

 const Payments = () => {
-	const {
-		orders = [],
-		isRequesting,
-		isError,
-	} = useSelect( ( select ) => {
-		const { getOrders, hasFinishedResolution, getOrdersError } =
-			select( ordersStore );
+	const { orders } = useSelect( ( select ) => {
+		const { getOrders } = select( ordersStore );

 		const query = {
 			page: 1,
 			per_page: 10,
 		};
-		const orders = getOrders( query, null );
-		const isRequesting = hasFinishedResolution( 'getOrders', [ query ] );

 		return {
-			orders,
-			isError: Boolean( getOrdersError( orders ) ),
-			isRequesting,
+			orders: getOrders( query, null ),
 		};
 	} );

@@ -51,7 +42,7 @@ const Payments = () => {
 			const updatedOrder = await apiFetch( {
 				path: `/wc/v3/orders/${ order.id }`,
 				method: 'PUT',
-				data: data,
+				data,
 				headers: {
 					'Content-Type': 'application/json',
 				},
@@ -62,8 +53,8 @@ const Payments = () => {
 		}
 	};

-	const renderOrders = ( orders ) => {
-		return orders.map( ( order ) => {
+	const renderOrders = ( orderList ) => {
+		return orderList.map( ( order ) => {
 			return (
 				<tr key={ order.id }>
 					<td className="manage-column column-thumb" key={ 0 }>
@@ -144,15 +135,9 @@ const Payments = () => {
 						</td>
 					</tr>
 				</thead>
-				<tbody>
-					{ ! isRequesting &&
-						orders?.length &&
-						renderOrders( orders ) }
-				</tbody>
+				<tbody>{ orders?.length > 0 && renderOrders( orders ) }</tbody>
 			</table>
-			{ ! isRequesting && orders?.length === 0 && (
-				<p>No orders found.</p>
-			) }
+			{ orders?.length === 0 && <p>No orders found.</p> }
 		</>
 	);
 };
diff --git a/plugins/woocommerce-beta-tester/src/rest-api-filters/index.js b/plugins/woocommerce-beta-tester/src/rest-api-filters/index.js
index 67628d61b04..91fc55cc021 100644
--- a/plugins/woocommerce-beta-tester/src/rest-api-filters/index.js
+++ b/plugins/woocommerce-beta-tester/src/rest-api-filters/index.js
@@ -169,9 +169,8 @@ export default compose(
 		};
 	} ),
 	withDispatch( ( dispatch ) => {
-		const { saveFilter, deleteFilter, toggleFilter } = dispatch(
-			STORE_KEY
-		);
+		const { saveFilter, deleteFilter, toggleFilter } =
+			dispatch( STORE_KEY );

 		return {
 			saveFilter,
diff --git a/plugins/woocommerce-beta-tester/src/tools/commands/load-template-version.js b/plugins/woocommerce-beta-tester/src/tools/commands/load-template-version.js
index e71258d80bc..5941f731306 100644
--- a/plugins/woocommerce-beta-tester/src/tools/commands/load-template-version.js
+++ b/plugins/woocommerce-beta-tester/src/tools/commands/load-template-version.js
@@ -37,24 +37,6 @@ export const LoadTemplateVersion = () => {
 	const selectedTemplate = params.template_name || '';
 	const selectedVersion = params.version || '';

-	// Load templates on component mount
-	useEffect( () => {
-		loadTemplates();
-	}, [] );
-
-	// Load versions when template changes
-	useEffect( () => {
-		if ( selectedTemplate ) {
-			loadVersions( selectedTemplate );
-		} else {
-			setVersions( [] );
-			updateCommandParams( LOAD_TEMPLATE_VERSION_ACTION_NAME, {
-				template_name: selectedTemplate,
-				version: '',
-			} );
-		}
-	}, [ selectedTemplate ] );
-
 	/**
 	 * Load available templates
 	 */
@@ -76,6 +58,7 @@ export const LoadTemplateVersion = () => {
 			setTemplates( options );
 			setIsLoadingTemplates( false );
 		} catch ( error ) {
+			// eslint-disable-next-line no-console
 			console.error( 'Error loading templates:', error );
 			setIsLoadingTemplates( false );
 		}
@@ -108,11 +91,30 @@ export const LoadTemplateVersion = () => {
 			}
 			setIsLoadingVersions( false );
 		} catch ( error ) {
+			// eslint-disable-next-line no-console
 			console.error( 'Error loading versions:', error );
 			setIsLoadingVersions( false );
 		}
 	};

+	// Load templates on component mount
+	useEffect( () => {
+		loadTemplates();
+	}, [] );
+
+	// Load versions when template changes
+	useEffect( () => {
+		if ( selectedTemplate ) {
+			loadVersions( selectedTemplate );
+		} else {
+			setVersions( [] );
+			updateCommandParams( LOAD_TEMPLATE_VERSION_ACTION_NAME, {
+				template_name: selectedTemplate,
+				version: '',
+			} );
+		}
+	}, [ selectedTemplate ] );
+
 	/**
 	 * Handle template selection change
 	 *
diff --git a/plugins/woocommerce-beta-tester/src/tools/data/index.js b/plugins/woocommerce-beta-tester/src/tools/data/index.js
index ea99d3b9ebd..6aae6bfea37 100644
--- a/plugins/woocommerce-beta-tester/src/tools/data/index.js
+++ b/plugins/woocommerce-beta-tester/src/tools/data/index.js
@@ -12,11 +12,11 @@ import * as selectors from './selectors';
 // @ts-expect-error These files are not TypeScript files.
 import reducer from './reducer';
 import { STORE_KEY } from './constants';
-export const store = createReduxStore(STORE_KEY, {
-    reducer,
-    actions,
-    controls,
-    selectors,
-    resolvers,
-});
-register(store);
+export const store = createReduxStore( STORE_KEY, {
+	reducer,
+	actions,
+	controls,
+	selectors,
+	resolvers,
+} );
+register( store );