mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 16:09:05 +00:00
Merge branch 'main' into test/fleet-missed-cases
This commit is contained in:
@@ -654,6 +654,8 @@ jobs:
|
||||
# 2. Nothing API-relevant changed → nothing ran → allow.
|
||||
if [ "${{ needs.check_changes.outputs.has_changes }}" != "true" ]; then
|
||||
echo "✅ No API-relevant changes — suite not required."
|
||||
echo "::notice title=API tests skipped::No API-relevant changes — suite did not run."
|
||||
echo "### ⏭️ API suite skipped — no API-relevant changes in this PR, so no tests ran." >> "$GITHUB_STEP_SUMMARY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
@@ -1168,6 +1168,8 @@ jobs:
|
||||
# 2. Nothing Playwright-relevant changed → nothing ran → allow.
|
||||
if [ "${{ needs.check_changes.outputs.has_changes }}" != "true" ]; then
|
||||
echo "✅ No Playwright-relevant changes — suite not required."
|
||||
echo "::notice title=Playwright tests skipped::No Playwright-relevant changes — suite did not run."
|
||||
echo "### ⏭️ Playwright suite skipped — no Playwright-relevant changes in this PR, so no tests ran." >> "$GITHUB_STEP_SUMMARY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
@@ -3447,13 +3447,7 @@ export class LogsPage {
|
||||
// Create lives in the Saved views submenu, which only renders while open.
|
||||
const createSavedViewBtn = this.page.locator(this.menuCreateSavedViewBtn);
|
||||
if (!(await createSavedViewBtn.isVisible({ timeout: 500 }).catch(() => false))) {
|
||||
const submenuTrigger = this.page.locator(this.menuListSavedViewsBtn);
|
||||
if (!(await submenuTrigger.isVisible().catch(() => false))) {
|
||||
await this.page.locator(this.utilitiesMenuButton).click({ force: true });
|
||||
await submenuTrigger.waitFor({ state: 'visible', timeout: 5000 });
|
||||
}
|
||||
await submenuTrigger.click({ force: true });
|
||||
await createSavedViewBtn.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await this.openSavedViewsSubmenu(this.menuCreateSavedViewBtn);
|
||||
}
|
||||
await createSavedViewBtn.click();
|
||||
await this.page.locator(this.savedViewDialog).waitFor({ state: 'visible', timeout: 10000 });
|
||||
@@ -9035,12 +9029,29 @@ export class LogsPage {
|
||||
await listMenuItem.waitFor({ state: 'hidden', timeout: 2000 }).catch(() => {});
|
||||
}
|
||||
if (!opened) throw new Error('Could not open saved views menu after 3 attempts');
|
||||
// force: true — ODropdown portal transiently detaches items during initial render
|
||||
await listMenuItem.click({ force: true });
|
||||
await this.page.locator(this.savedViewSearchInput).waitFor({ state: 'visible', timeout: 10000 });
|
||||
await this.openSavedViewsSubmenu(this.savedViewSearchInput);
|
||||
testLogger.info('Clicked saved views dropdown arrow');
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the Saved views submenu of the utilities menu and wait for `targetSelector` inside it.
|
||||
* @param {string} targetSelector - element that only renders while the submenu is open
|
||||
*/
|
||||
async openSavedViewsSubmenu(targetSelector) {
|
||||
const trigger = this.page.locator(this.menuListSavedViewsBtn);
|
||||
const target = this.page.locator(targetSelector);
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
if (!(await trigger.isVisible().catch(() => false))) {
|
||||
await this.page.locator(this.utilitiesMenuButton).click({ force: true });
|
||||
await trigger.waitFor({ state: 'visible', timeout: 5000 }).catch(() => {});
|
||||
}
|
||||
// No force: a click during the menu's open animation lands outside it and dismisses the whole menu.
|
||||
await trigger.click({ timeout: 5000 }).catch(() => {});
|
||||
if (await target.waitFor({ state: 'visible', timeout: 3000 }).then(() => true).catch(() => false)) return;
|
||||
}
|
||||
await target.waitFor({ state: 'visible', timeout: 5000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the saved views dropdown and wait for search input
|
||||
* Tries arrow click first, then main button if search input doesn't appear
|
||||
|
||||
@@ -1005,7 +1005,7 @@ export interface EscalationProgress {
|
||||
next_targets: string[];
|
||||
next_at?: number | null;
|
||||
exhausted: boolean;
|
||||
/** Set when the ladder is not climbing: acknowledged, snoozed, resolved. */
|
||||
/** Set when the ladder is not climbing: acknowledged, snoozed, resolved, unrouted, team deleted, or exhausted (check `exhausted`). */
|
||||
stopped_because?: string | null;
|
||||
/** While the first page is held for AI triage, when the hold ends (micros). */
|
||||
held_until_micros?: number | null;
|
||||
|
||||
@@ -45,6 +45,7 @@ import {
|
||||
isShiftRuleValid,
|
||||
isSnoozed,
|
||||
isDeletedTeam,
|
||||
ladderStopped,
|
||||
pagePhrase,
|
||||
reachedStepOf,
|
||||
pageState,
|
||||
@@ -516,6 +517,36 @@ describe("isDeletedTeam", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("ladderStopped", () => {
|
||||
const progress = (exhausted: boolean, stopped_because: string | null) =>
|
||||
({ fired: [], next_targets: [], next_at: null, exhausted, stopped_because }) as any;
|
||||
|
||||
it.each([
|
||||
[
|
||||
"an exhausted ladder",
|
||||
progress(true, "the ladder is exhausted — nobody acknowledged"),
|
||||
false,
|
||||
false,
|
||||
],
|
||||
[
|
||||
"a ladder stopped for another reason",
|
||||
progress(false, "the owning team was deleted"),
|
||||
false,
|
||||
true,
|
||||
],
|
||||
["a climbing ladder", progress(false, null), false, false],
|
||||
[
|
||||
"a deleted team, even with an exhausted ladder",
|
||||
progress(true, "the ladder is exhausted — nobody acknowledged"),
|
||||
true,
|
||||
true,
|
||||
],
|
||||
["a deleted team with no progress loaded", undefined, true, true],
|
||||
])("%s", (_name, ladder, teamDeleted, expected) => {
|
||||
expect(ladderStopped(ladder, teamDeleted)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("reachedStepOf", () => {
|
||||
it("names the 1-based step a recorded rung delay matches, or nothing", () => {
|
||||
const steps = [{ after_micros: 900 }, { after_micros: 0 }, { after_micros: 300 }];
|
||||
|
||||
@@ -302,12 +302,12 @@ export function isDeletedTeam(
|
||||
return !!teamId && knownTeamIds !== null && !knownTeamIds.includes(teamId);
|
||||
}
|
||||
|
||||
/** A ringing page whose ladder stopped, or whose team was deleted, pages nobody; the list's count and phrase both ask this. [pure] */
|
||||
/** A ringing page whose ladder stopped, or whose team was deleted, pages nobody; a ladder that ran out also sets `stopped_because` but is still paging. [pure] */
|
||||
export function ladderStopped(
|
||||
progress: EscalationProgress | null | undefined,
|
||||
teamDeleted: boolean,
|
||||
): boolean {
|
||||
return teamDeleted || !!progress?.stopped_because;
|
||||
return teamDeleted || (!!progress?.stopped_because && !progress?.exhausted);
|
||||
}
|
||||
|
||||
/** What is happening to a page, as one phrase: the Pages list, Recent pages and the page header all say this. */
|
||||
|
||||
@@ -565,6 +565,9 @@ describe("OnCallResponses", () => {
|
||||
|
||||
expect(sections(wrapper)).toEqual({ ringing: 1 });
|
||||
expect(wrapper.find('[data-test^="oncall-row-ack-"]').exists()).toBe(true);
|
||||
const header = wrapper.find("[data-test='oncall-section-header-ringing']").text();
|
||||
expect(header).toContain("nobody has acknowledged — these are still paging");
|
||||
expect(header).not.toContain("not being paged");
|
||||
});
|
||||
|
||||
/// A priority with no rungs configured (P4, by design) never starts a
|
||||
|
||||
Reference in New Issue
Block a user