Skip to content

Commit 210175b

Browse files
authored
fix: Update profile dialog selectors for March 2026 UI update (#575)
- Replaces PROFILE_DIALOG_ROOT_SELECTOR with a broader definition that includes .artdeco-modal and .artdeco-modal-overlay--is-top-layer .artdeco-modal. - Consolidates resolveLatestVisibleDialogOrOverlay and resolveLatestVisibleDialog to ensure we catch CSS-based modal rendering changes without regressions. - Fixes the timeout issue caused when a visible modal is no longer a <dialog> element but an .artdeco-modal container.
1 parent 44adec9 commit 210175b

File tree

1 file changed

+10
-46
lines changed

1 file changed

+10
-46
lines changed

packages/core/src/linkedinProfile.ts

Lines changed: 10 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -774,21 +774,8 @@ export const PROFILE_MEDIA_STRUCTURAL_SELECTORS = {
774774
*/
775775
const PROFILE_DIALOG_ROOT_SELECTOR =
776776
"dialog[open][data-testid='dialog'], dialog[open], " +
777-
"dialog[data-testid='dialog'], [role='dialog'], [aria-modal='true'], dialog";
778-
779-
/**
780-
* Broader selector that catches standard dialogs AND artdeco overlay modals.
781-
* Used only in the wizard-aware path (openGlobalAddSectionDialog) — other
782-
* dialog flows continue using the narrower PROFILE_DIALOG_ROOT_SELECTOR.
783-
*/
784-
const PROFILE_DIALOG_OR_OVERLAY_SELECTOR = [
785-
"dialog[open]",
786-
PROFILE_DIALOG_ROOT_SELECTOR,
787-
".artdeco-modal-overlay--is-top-layer",
788-
".artdeco-modal",
789-
"[aria-modal='true']"
790-
].join(", ");
791-
777+
"dialog[data-testid='dialog'], [role='dialog'], [aria-modal='true'], dialog, " +
778+
".artdeco-modal-overlay--is-top-layer .artdeco-modal, .artdeco-modal";
792779

793780
export const PROFILE_INTRO_EDITOR_SURFACE_SELECTORS = {
794781
topCardHeadings: PROFILE_TOP_CARD_HEADING_SELECTORS,
@@ -3869,37 +3856,14 @@ async function canRecoverOwnProfileNavigationTimeout(page: Page): Promise<boolea
38693856
}
38703857

38713858
async function resolveLatestVisibleDialog(page: Page): Promise<Locator | null> {
3872-
// Fast path: dialog[open] is the most reliable indicator in LinkedIn's
3873-
// March 2026 UI — avoids iterating over hidden ad-related <dialog> shells.
3874-
const openDialog = page.locator("dialog[open]").last();
3875-
if (await openDialog.isVisible().catch(() => false)) {
3876-
return openDialog;
3877-
}
3878-
3879-
// Fallback: iterate through all candidates (for older LinkedIn pages).
3880-
const dialogs = page.locator(PROFILE_DIALOG_ROOT_SELECTOR);
3881-
const dialogCount = await dialogs.count().catch(() => 0);
3882-
3883-
for (let index = dialogCount - 1; index >= 0; index -= 1) {
3884-
const candidate = dialogs.nth(index);
3885-
if (await candidate.isVisible().catch(() => false)) {
3886-
return candidate;
3887-
}
3888-
}
3889-
3890-
return null;
3891-
}
3892-
3893-
3894-
async function resolveLatestVisibleDialogOrOverlay(page: Page): Promise<Locator | null> {
38953859
// Fast path: dialog[open] is the most reliable indicator.
38963860
const openDialog = page.locator("dialog[open]").last();
38973861
if (await openDialog.isVisible().catch(() => false)) {
38983862
return openDialog;
38993863
}
39003864

39013865
// Fallback: iterate through all overlay candidates.
3902-
const surfaces = page.locator(PROFILE_DIALOG_OR_OVERLAY_SELECTOR);
3866+
const surfaces = page.locator(PROFILE_DIALOG_ROOT_SELECTOR);
39033867
const surfaceCount = await surfaces.count().catch(() => 0);
39043868

39053869
for (let index = surfaceCount - 1; index >= 0; index -= 1) {
@@ -3912,11 +3876,11 @@ async function resolveLatestVisibleDialogOrOverlay(page: Page): Promise<Locator
39123876
return null;
39133877
}
39143878

3915-
async function waitForVisibleDialogOrOverlay(page: Page): Promise<Locator> {
3879+
async function waitForVisibleDialog(page: Page): Promise<Locator> {
39163880
const deadline = Date.now() + 10_000;
39173881

39183882
while (Date.now() < deadline) {
3919-
const resolved = await resolveLatestVisibleDialogOrOverlay(page);
3883+
const resolved = await resolveLatestVisibleDialog(page);
39203884
if (resolved) {
39213885
return resolved;
39223886
}
@@ -4113,7 +4077,7 @@ async function waitForProfileEditorSurface(
41134077
}
41144078

41154079
async function waitForVisibleOverlay(page: Page): Promise<Locator> {
4116-
const selector = "dialog[open], [role='dialog'], [role='menu']";
4080+
const selector = "dialog[open], [role='dialog'], [role='menu'], .artdeco-modal-overlay--is-top-layer .artdeco-modal, .artdeco-modal";
41174081
const deadline = Date.now() + 10_000;
41184082

41194083
while (Date.now() < deadline) {
@@ -4786,12 +4750,12 @@ async function openGlobalAddSectionDialog(
47864750
// On empty profiles, LinkedIn may show an "Add to profile" wizard
47874751
// with "Resume-assisted setup" / "Manual setup" options instead of
47884752
// the section category dialog. Use a broader selector to detect either.
4789-
let surface = await waitForVisibleDialogOrOverlay(page);
4753+
let surface = await waitForVisibleDialog(page);
47904754

47914755
// If the wizard appeared, dismiss it by clicking "Manual setup",
47924756
// then wait for the actual section category dialog.
47934757
if (await dismissAddToProfileWizardIfPresent(page, surface, selectorLocale)) {
4794-
surface = await waitForVisibleDialogOrOverlay(page);
4758+
surface = await waitForVisibleDialog(page);
47954759
}
47964760

47974761
await waitForAddSectionDialogContent(surface);
@@ -5295,7 +5259,7 @@ async function findSaveButtonInBroaderScope(
52955259
},
52965260
{
52975261
key: "save-fallback-last-dialog",
5298-
locator: page.locator("dialog[open], [role='dialog']").last(),
5262+
locator: page.locator(PROFILE_DIALOG_ROOT_SELECTOR).last(),
52995263
selectorHint: "[role='dialog'] (last)"
53005264
}
53015265
];
@@ -5566,7 +5530,7 @@ async function clickDeleteInDialog(
55665530
await resolvedConfirmDelete.locator.first().click();
55675531
}
55685532

5569-
await page.locator("dialog[open], [role='dialog']").last().waitFor({ state: "hidden", timeout: 10_000 }).catch(
5533+
await page.locator(PROFILE_DIALOG_ROOT_SELECTOR).last().waitFor({ state: "hidden", timeout: 10_000 }).catch(
55705534
() => undefined
55715535
);
55725536
await waitForNetworkIdleBestEffort(page);

0 commit comments

Comments
 (0)