Commit 9c392cdaa19 for woocommerce

commit 9c392cdaa19f552141d62c0c6b0d834deeb0229b
Author: Brandon Kraft <public@brandonkraft.com>
Date:   Tue Mar 3 17:58:23 2026 -0600

    Fix live-branches userscript for GitHub's updated DOM (#63388)

    * Fix live-branches userscript for GitHub's new React-based PR pages

    GitHub removed the `.head-ref` and `.gh-header-meta .State` CSS classes
    that this script relied on to extract branch names and PR status,
    causing the Jurassic Ninja URL to have an empty branch parameter.

    Port the same fix already applied in the Jetpack equivalent script:
    extract PR data from GitHub's embedded JSON (`react-app.embeddedData`)
    with a fallback to the legacy DOM selectors.

    Also fix checkbox change listeners that were never attached because
    an arrow function in `.each()` doesn't bind `this` to the element.

    * Address review feedback on live-branches userscript

    - Use $ alias instead of bare jQuery in legacy fallback, and trim branch name
    - Extract isDraft from embedded JSON for robust draft detection
    - Keep DOM selector as final fallback for draft status

diff --git a/plugins/woocommerce-beta-tester/changelog/fix-live-branches-github-selectors b/plugins/woocommerce-beta-tester/changelog/fix-live-branches-github-selectors
new file mode 100644
index 00000000000..65c4cca0cda
--- /dev/null
+++ b/plugins/woocommerce-beta-tester/changelog/fix-live-branches-github-selectors
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix live-branches userscript broken by GitHub DOM changes: extract PR data from embedded JSON instead of removed CSS selectors, and fix checkbox change listeners that were never attached due to arrow function misuse.
diff --git a/plugins/woocommerce-beta-tester/userscripts/wc-live-branches.user.js b/plugins/woocommerce-beta-tester/userscripts/wc-live-branches.user.js
index c1930704d0d..6dbf992fc85 100644
--- a/plugins/woocommerce-beta-tester/userscripts/wc-live-branches.user.js
+++ b/plugins/woocommerce-beta-tester/userscripts/wc-live-branches.user.js
@@ -1,7 +1,7 @@
 // ==UserScript==
 // @name         WooCommerce Live Branches
 // @namespace    https://wordpress.com/
-// @version      1.1.1
+// @version      1.2.0
 // @description  Adds links to PRs pointing to Jurassic Ninja sites for live-testing a changeset
 // @grant        GM_xmlhttpRequest
 // @connect      jurassic.ninja
@@ -62,6 +62,55 @@
 		return m && m[ 1 ] ? decodeURIComponent( m[ 1 ] ) : null;
 	}

+	/**
+	 * Determine PR data from GitHub's embedded JSON.
+	 *
+	 * @return {object|null} Data with currentBranch and branchStatus, or null.
+	 */
+	function getPRDataFromEmbeddedJson() {
+		const el = document.querySelector(
+			'[data-target="react-app.embeddedData"]'
+		);
+		if ( ! el ) {
+			return null;
+		}
+		try {
+			const data = JSON.parse( el.textContent );
+			const pr =
+				data?.payload?.pullRequestsLayoutRoute?.pullRequest;
+			if ( pr?.headBranch && pr?.state ) {
+				return {
+					currentBranch: pr.headBranch,
+					branchStatus: pr.state,
+					headRepoOwner: pr.headRepositoryOwnerLogin || '',
+					isDraft: !! pr.isDraft,
+				};
+			}
+		} catch ( e ) {
+			// Fall through to legacy method.
+		}
+		return null;
+	}
+
+	/**
+	 * Determine PR data from legacy DOM selectors.
+	 *
+	 * @return {object|null} Data with currentBranch and branchStatus, or null.
+	 */
+	function getPRDataFromDom() {
+		const currentBranch = $( '.head-ref:first' ).text().trim();
+		if ( ! currentBranch ) {
+			return null;
+		}
+		const branchStatus = $( '.gh-header-meta .State' )
+			.text()
+			.trim();
+		if ( ! branchStatus ) {
+			return null;
+		}
+		return { currentBranch, branchStatus, headRepoOwner: '', isDraft: false };
+	}
+
 	/** Function. */
 	function doit() {
 		const markdownBody =
@@ -75,12 +124,23 @@
 		}

 		const host = 'https://jurassic.ninja';
-		const currentBranch = jQuery( '.head-ref:first' ).text();
-		const branchIsForked = currentBranch.includes( ':' );
-		const branchStatus = $( '.gh-header-meta .State' ).text().trim();
 		const repo = determineRepo();
+		const prData =
+			getPRDataFromEmbeddedJson() ?? getPRDataFromDom();
+		if ( ! prData ) {
+			appendHtml(
+				markdownBody,
+				'<p><strong>Failed to find PR data. The WooCommerce Live Branches script may need updating.</strong></p>'
+			);
+			return;
+		}
+		const { currentBranch, branchStatus } = prData;
+		const branchIsForked = prData.headRepoOwner
+			? prData.headRepoOwner !==
+				( repo ? repo.split( '/' )[ 0 ] : '' )
+			: currentBranch.includes( ':' );

-		if ( branchStatus === 'Merged' ) {
+		if ( branchStatus === 'MERGED' || branchStatus === 'Merged' ) {
 			const contents = `
 				<p><strong>This branch is already merged.</strong></p>
 				<p><a target="_blank" rel="nofollow noopener" href="${ getLink() }">
@@ -88,7 +148,11 @@
 				</a></p>
 			`;
 			appendHtml( markdownBody, contents );
-		} else if ( branchStatus === 'Draft' ) {
+		} else if (
+			branchStatus === 'Draft' ||
+			prData.isDraft ||
+			document.querySelector( '[data-status="draft"]' )
+		) {
 			appendHtml(
 				markdownBody,
 				'<p><strong>This branch is a draft. You can open live branches only from open pull requests.</strong></p>'
@@ -350,9 +414,7 @@
 			$el.append( liveBranches );
 			liveBranches
 				.find( 'input[type=checkbox]' )
-				.each( () =>
-					this.addEventListener( 'change', onInputChanged )
-				);
+				.on( 'change', onInputChanged );
 		}

 		/**