From 93ececb7206dae4a99392ac3105b2a71537bb6de Mon Sep 17 00:00:00 2001 From: Max Mulatz Date: Wed, 29 Jan 2025 11:03:49 +0100 Subject: [PATCH] [#443] GitHub adapter broken after GitHub redesign This updates the GitHub adapter to work with the redesigned issue pages. As we do not know now GitHub Enterprise looks like for the majority of users, we keep the previous logic for the "legacy" strategy of the adapter. To keep the change and the adapter simple, I dropped support selecting issues on the issues page. Instead, we only support the issue show pages now. On the long run, it may make sense to look into supporting GitHub projects too. The new GitHub adapter supports GitHub issue types and won't look for the "bug" label any more. Ticket: /~https://github.com/bitcrowd/tickety-tick/issues/443 Fixes: #443 --- src/core/adapters/dom-helpers.ts | 10 +-- src/core/adapters/github.test.ts | 105 ++++++++++--------------------- src/core/adapters/github.ts | 54 +++++----------- 3 files changed, 55 insertions(+), 114 deletions(-) diff --git a/src/core/adapters/dom-helpers.ts b/src/core/adapters/dom-helpers.ts index addeee44..c79cc340 100644 --- a/src/core/adapters/dom-helpers.ts +++ b/src/core/adapters/dom-helpers.ts @@ -4,26 +4,26 @@ export function trim(string: string | null) { // Finders -export function $find(selector: string, context: HTMLDocument | HTMLElement) { +export function $find(selector: string, context: Document | HTMLElement) { return context.querySelector(selector); } -export function $all(selector: string, context: HTMLDocument | HTMLElement) { +export function $all(selector: string, context: Document | HTMLElement) { return Array.from(context.querySelectorAll(selector)); } -export function $has(selector: string, context: HTMLDocument | HTMLElement) { +export function $has(selector: string, context: Document | HTMLElement) { return $find(selector, context) !== null; } // Properties -export function $text(selector: string, context: HTMLDocument | HTMLElement) { +export function $text(selector: string, context: Document | HTMLElement) { const node = $find(selector, context); return node ? trim(node.textContent) : null; } -export function $value(selector: string, context: HTMLDocument | HTMLElement) { +export function $value(selector: string, context: Document | HTMLElement) { const node = $find(selector, context); return node ? trim(node.getAttribute("value")) : null; } diff --git a/src/core/adapters/github.test.ts b/src/core/adapters/github.test.ts index 7b0dde26..317d3945 100644 --- a/src/core/adapters/github.test.ts +++ b/src/core/adapters/github.test.ts @@ -9,84 +9,59 @@ import scan, { selectors } from "./github"; const pages = { default: { issuepage: ` -
-

- A Random GitHub Issue - #12 -

+
+
+

+ A Random GitHub Issue + #12 +

+
`, bugpage: ` -
-

- A Random GitHub Issue - #12 -

-
- bug +
+
+
+

+ A Random GitHub Issue + #12 +

+
-
`, - indexpage: ` -
- `, }, legacy: { issuepage: ` -
+

A Random GitHub Issue #12

`, - bugpage: ` -
+

A Random GitHub Issue #12

- `, - - indexpage: ` - `, }, }; @@ -119,7 +94,7 @@ Object.keys(selectors).forEach((variant) => { ]); }); - it("recognizes issues labelled as bugs", async () => { + it("recognizes issue types", async () => { const result = await scan(url("issues/12"), doc(html.bugpage)); expect(result).toEqual([ { @@ -130,17 +105,5 @@ Object.keys(selectors).forEach((variant) => { }, ]); }); - - it("extracts tickets from issues index pages", async () => { - const result = await scan(url("issues"), doc(html.indexpage)); - expect(result).toEqual([ - { - id: "12", - title: "A Selected GitHub Issue", - type: "bug", - url: "/~https://github.com/test-org/test-project/issues/12", - }, - ]); - }); }); }); diff --git a/src/core/adapters/github.ts b/src/core/adapters/github.ts index 0b7c23dd..ef55e6a1 100644 --- a/src/core/adapters/github.ts +++ b/src/core/adapters/github.ts @@ -5,7 +5,7 @@ */ import type { TicketData } from "../types"; -import { $all, $has, $text, $value } from "./dom-helpers"; +import { $has, $text } from "./dom-helpers"; import { hasRequiredDetails } from "./utils"; /** @@ -17,16 +17,14 @@ import { hasRequiredDetails } from "./utils"; */ export const selectors = { default: { - issuesPage: ".js-check-all-container .js-issue-row.selected", - issuesPageLabel: ".lh-default .IssueLabel", - issuePage: ".js-issues-results .gh-header-number", - issuePageLabel: '.js-issue-labels .IssueLabel[data-name="bug" i]', + issuePage: 'div[data-testid="issue-viewer-container"]', + issueId: 'div[data-component="TitleArea"] span', + issueTitle: 'div[data-component="TitleArea"] bdi.markdown-title', }, legacy: { - issuesPage: ".issues-listing .js-issue-row.selected", - issuesPageLabel: ".labels .label", - issuePage: ".issues-listing .gh-header-number", - issuePageLabel: '.sidebar-labels .label[title="bug"]', + issuePage: ".js-issues-results .gh-header-number", + issueId: ".gh-header-number", + issueTitle: ".js-issue-title", }, }; @@ -34,40 +32,20 @@ async function attempt( doc: Document, select: (typeof selectors)[keyof typeof selectors], ) { - // issue list page - if ($has(select.issuesPage, doc)) { - const issues = $all(select.issuesPage, doc); - - const tickets = issues.reduce( - (acc, issue) => { - const id = $value("input.js-issues-list-check", issue); - const title = $text("a.js-navigation-open", issue); - - if (!id || !title) return acc; - - const labels = $all(select.issuesPageLabel, issue); - const type = labels.some((l) => /bug/i.test(`${l.textContent}`)) - ? "bug" - : "feature"; - - const ticket = { id, title, type }; - acc.push(ticket); - return acc; - }, - [], - ); - - return tickets; - } - // single issue page if ($has(select.issuePage, doc)) { - const id = $text(".gh-header-number", doc)?.replace(/^#/, ""); - const title = $text(".js-issue-title", doc); + const id = $text(select.issueId, doc)?.replace(/^#/, ""); + const title = $text(select.issueTitle, doc); if (!id || !title) return []; - const type = $has(select.issuePageLabel, doc) ? "bug" : "feature"; + const type = + $text('div[data-testid="issue-type-container"]', doc) + ?.toLowerCase() + .trim() || $has('.js-issue-labels .IssueLabel[data-name="bug" i]', doc) + ? "bug" + : "feature"; + const tickets = [{ id, title, type }]; return tickets; }