Repository: refined-github/refined-github
Pull request #8164 (2024-12-18 07:42:22)

`closing-remarks` - Support new PR merge box
- For #8123 


## Test URLs

Any open PR, like this page

## Screenshot

Gif also shows other auto-deletion working, but that's a separate PR

![merging 2](https://github.com/user-attachments/assets/f3a65013-63be-48b8-a91b-7b19ae3cb0dc)



Patch:
diff --git a/source/github-events/on-pr-merge.ts b/source/github-events/on-pr-merge.ts
index 16b49f2b..ad5399ef 100644
--- a/source/github-events/on-pr-merge.ts
+++ b/source/github-events/on-pr-merge.ts
@@ -4,6 +4,29 @@ import observe from '../helpers/selector-observer.js';
 
 // This event ensures that the callback appears exclusively to the person that merged the PR and not anyone who was on the page at the time of the merge
 export default async function onPrMerge(callback: VoidFunction, signal: AbortSignal): Promise<void> {
-	await oneEvent('.js-merge-commit-button', 'click', {signal});
-	observe('.TimelineItem-badge .octicon-git-merge', callback, {signal});
+	// It must start listening early or else the animation ID will be generated incorrectly (ancestor)
+	// WARNING: Be very careful about the value of ancestor if you refactor this code
+	const mergeEvent = new Promise(resolve => {
+		// `emphasis` excludes merge commit icons added by `mark-merge-commits-in-list`
+		observe('.TimelineItem-badge.color-fg-on-emphasis .octicon-git-merge', resolve, {ancestor: 4});
+	});
+
+	await oneEvent([
+		// TODO: Drop in May 2025
+		'.js-merge-commit-button',
+
+		// `:has(textarea)` excludes the first "Merge pull request" button that only opens the box
+		// TODO: Add a textContent check after https://github.com/fregante/delegate-it/issues/55
+		// TODO: Support "Confirm auto-merge (squash)" button (it's not primary/green)
+		'[aria-label="Checks"] ~ div:has(textarea) button[data-size="medium"][data-variant="primary"]',
+	], 'click', {signal});
+
+	// It won't resolve once the signal is aborted
+	await mergeEvent;
+
+	if (signal.aborted) {
+		throw new Error('The code shouldn’t have reached this point');
+	} else {
+		callback();
+	}
 }
diff --git a/source/helpers/is-low-quality-comment.ts b/source/helpers/is-low-quality-comment.ts
index 19b57bf9..17c10820 100644
--- a/source/helpers/is-low-quality-comment.ts
+++ b/source/helpers/is-low-quality-comment.ts
@@ -1,4 +1,37 @@
+const lowQualityWords = [
+	'a',
+	'an',
+	'and',
+	'any',
+	'bump',
+	'dito',
+	'ditto',
+	'following',
+	'for',
+	'here',
+	'is',
+	'issue',
+	'issues',
+	'me',
+	'on',
+	'please',
+	'pls',
+	'plz',
+	'question',
+	'same',
+	'solution',
+	'still',
+	'there',
+	'this',
+	'too',
+	'update',
+	'updates',
+];
+const lowQualityWordsRegex = new RegExp(`\\b(${lowQualityWords.join('|')})\\b`, 'gi');
+
 export default function isLowQualityComment(text: string): boolean {
 	// Note: the unicode range targets skin color modifiers for the hand emojis
-	return text.replaceAll(/[\s,.!?👍👎👌🙏]+|[\u{1F3FB}-\u{1F3FF}]|[+-]\d+|⬆️|ditt?o|me|too|t?here|on|same|this|issues?|please|pl[sz]|any|updates?|bump|question|solution|following/giu, '') === '';
+	return text
+		.replaceAll(lowQualityWordsRegex, '')
+		.replaceAll(/[\s,.!?👍👎👌🙏]+|[\u{1F3FB}-\u{1F3FF}]|[+-]\d+|⬆️/gu, '') === '';
 }
diff --git a/source/helpers/selector-observer.tsx b/source/helpers/selector-observer.tsx
index 81cee41a..f3e39e53 100644
--- a/source/helpers/selector-observer.tsx
+++ b/source/helpers/selector-observer.tsx
@@ -16,6 +16,8 @@ type Options = {
 	stopOnDomReady?: boolean;
 	once?: boolean;
 	signal?: AbortSignal;
+	/** Refer to getCallerID's documentation */
+	ancestor?: number;
 };
 
 const animation = 'rgh-selector-observer';
@@ -30,7 +32,7 @@ export default function observe<
 >(
 	selectors: Selector | readonly Selector[],
 	listener: ObserverListener<ExpectedElement>,
-	{signal, stopOnDomReady, once}: Options = {},
+	{signal, stopOnDomReady, once, ancestor}: Options = {},
 ): void {
 	if (signal?.aborted) {
 		return;
@@ -46,7 +48,7 @@ export default function observe<
 	}
 
 	const selector = typeof selectors === 'string' ? selectors : selectors.join(',\n');
-	const seenMark = 'rgh-seen-' + getCallerID();
+	const seenMark = 'rgh-seen-' + getCallerID(ancestor);
 
 	registerAnimation();
 
@@ -98,7 +100,7 @@ export default function observe<
 	}, {once, signal});
 }
 
-// Untested
+// Untested, likely breaks due to wrong `ancestor` level
 export async function waitForElement<
 	Selector extends string,
 	ExpectedElement extends ParseSelector<Selector, HTMLElement | SVGElement>,


Test patch:
diff --git a/source/helpers/is-low-quality-comment.test.ts b/source/helpers/is-low-quality-comment.test.ts
index c01c602a..2c79cc49 100644
--- a/source/helpers/is-low-quality-comment.test.ts
+++ b/source/helpers/is-low-quality-comment.test.ts
@@ -1,28 +1,45 @@
-import {test, assert} from 'vitest';
+import {describe, test, assert} from 'vitest';
 
 import isLowQualityComment from './is-low-quality-comment.js';
 
-test('isLowQualityComment', () => {
-	assert.isTrue(isLowQualityComment('+1'));
-	assert.isTrue(isLowQualityComment('+1!'));
-	assert.isTrue(isLowQualityComment('+10'));
-	assert.isTrue(isLowQualityComment('+9000'));
-	assert.isTrue(isLowQualityComment('-1'));
-	assert.isTrue(isLowQualityComment('👍'));
-	assert.isTrue(isLowQualityComment('👍🏾'));
-	assert.isTrue(isLowQualityComment('me too'));
-	assert.isTrue(isLowQualityComment('ditto'));
-	assert.isTrue(isLowQualityComment('Dito'));
-	assert.isTrue(isLowQualityComment('following'));
-	assert.isTrue(isLowQualityComment('please update!'));
-	assert.isTrue(isLowQualityComment('please update 🙏🏻'));
-	assert.isTrue(isLowQualityComment('same issue'));
-	assert.isTrue(isLowQualityComment('this same issues'));
-	assert.isTrue(isLowQualityComment('same question'));
-	assert.isTrue(isLowQualityComment('any updates there?'));
+describe('isLowQualityComment', () => {
+	describe('returns true', () => {
+		test.each([
+			'+1',
+			'+1!',
+			'+10',
+			'+9000',
+			'-1',
+			'👍',
+			'👍🏾',
+			'me too',
+			'ditto',
+			'Dito',
+			'following',
+			'please update!',
+			'please update 🙏🏻',
+			'same issue',
+			'this same issues',
+			'same question',
+			'any updates there?',
+			'any updates on this?',
+			'This is still an issue for me',
+			'Still an issue',
+			'Issue for me too.',
+			'and for me',
+		])('%s', comment => {
+			assert.isTrue(isLowQualityComment(comment));
+		});
+	});
 
-	assert.isFalse(isLowQualityComment('+1\n<some useful information>'));
-	assert.isFalse(isLowQualityComment('Same here. <some useful information>'));
-	assert.isFalse(isLowQualityComment('Same here, please update, thanks'));
-	assert.isFalse(isLowQualityComment('Same here! Please update, thank you.'));
+	describe('returns false', () => {
+		test.each([
+			'+1\n<some useful information>',
+			'Same here. <some useful information>',
+			'Same here, please update, thanks',
+			'Same here! Please update, thank you.',
+		])('%s', comment => {
+			assert.isFalse(isLowQualityComment(comment));
+		});
+	});
 });