Skip to content

Commit efdd6b9

Browse files
fix(e2e): handle other catalog sources when verifying disable in testSourceEnableDisable (#6734) (#7383)
* fix(e2e): handle other catalog sources when verifying disable in testSourceEnableDisable * fix(e2e): serialize catalog source toggles and handle remaining source * fix(bff): add retry logic for configmap conflicts on catalog source update * Revert "fix(bff): add retry logic for configmap conflicts on catalog source update" This reverts commit 97d2a3d33ab7495ca90ff22b60d8ad76dc2495b6. * fix(e2e): ensure catalog sources are enabled before testSourceEnableDisable runs * fix(e2e): improve catalog source toggle reliability with API waits and correct configmap precedence * fix(e2e): handle other catalog sources when verifying disable behavior Revert unnecessary changes (switch selector, intercept serialization, retryableBefore setup, configmap precedence, types cleanup) and keep only the fix for environments with additional catalog sources: use waitForModelCatalogAfterDisable to detect whether to expect cards or empty state after disabling the test sources. Made-with: Cursor * fix(e2e): serialize toggle clicks and ensure sources enabled on retry Two fixes for testSourceEnableDisable reliability: 1. Serialize disable toggles: verify each source is disabled in the configmap before clicking the next toggle. Back-to-back clicks cause a PATCH race condition in the BFF, leaving the second source still enabled and the catalog never reaching empty state. 2. Ensure sources are enabled in retryableBefore: on test retry, the sources may still be disabled from the previous run. Enable and verify both sources before the test starts. Made-with: Cursor * fix(e2e): disable 'Other' catalog source in testSourceEnableDisable The test was timing out because only two of three default sources (redhat_ai_models, redhat_ai_validated_models) were being disabled. The third default source (other_models) remained enabled, preventing the empty state from appearing in the catalog. Made-with: Cursor * fix(e2e): enable and verify Other source in retryableBefore setup Add redhatAiSourceId3 (other_models) to retryableBefore so all three sources are enabled and verified before the test runs, matching the after() teardown. Addresses review comments from FedeAlonso and ConorOM1. Made-with: Cursor --------- Co-authored-by: Conor O'Malley <97108400+ConorOM1@users.noreply.github.com>
1 parent 202ecd4 commit efdd6b9

4 files changed

Lines changed: 95 additions & 8 deletions

File tree

packages/cypress/cypress/fixtures/e2e/modelCatalog/testSourceEnableDisable.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ sourceName: 'Red Hat AI'
22
redhatAiSourceId: 'redhat_ai_models'
33
sourceName2: 'Red Hat AI validated'
44
redhatAiSourceId2: 'redhat_ai_validated_models'
5+
sourceName3: 'Other'
6+
redhatAiSourceId3: 'other_models'

packages/cypress/cypress/tests/e2e/modelCatalog/testSourceEnableDisable.cy.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { modelCatalog } from '../../../pages/modelCatalog/modelCatalog';
55
import {
66
verifyModelCatalogSourceEnabled,
77
waitForModelCatalogCards,
8-
waitForModelCatalogEmptyState,
8+
waitForModelCatalogAfterDisable,
99
enableModelCatalogSource,
1010
} from '../../../utils/oc_commands/modelCatalog';
1111
import { retryableBefore } from '../../../utils/retryableHooks';
@@ -19,13 +19,22 @@ describe('Verify Model Catalog Source Enable/Disable', () => {
1919
.fixture('e2e/modelCatalog/testSourceEnableDisable.yaml', 'utf8')
2020
.then((yamlContent: string) => {
2121
testData = yaml.load(yamlContent) as ModelCatalogSourceTestData;
22+
})
23+
.then(() => {
24+
enableModelCatalogSource(testData.redhatAiSourceId);
25+
enableModelCatalogSource(testData.redhatAiSourceId2);
26+
enableModelCatalogSource(testData.redhatAiSourceId3);
27+
verifyModelCatalogSourceEnabled(testData.redhatAiSourceId, true);
28+
verifyModelCatalogSourceEnabled(testData.redhatAiSourceId2, true);
29+
verifyModelCatalogSourceEnabled(testData.redhatAiSourceId3, true);
2230
});
2331
});
2432

2533
after(() => {
2634
cy.step('Re-enable model catalog sources via configmap');
2735
enableModelCatalogSource(testData.redhatAiSourceId);
2836
enableModelCatalogSource(testData.redhatAiSourceId2);
37+
enableModelCatalogSource(testData.redhatAiSourceId3);
2938
});
3039

3140
it(
@@ -56,20 +65,30 @@ describe('Verify Model Catalog Source Enable/Disable', () => {
5665
cy.step(`Disable the ${testData.sourceName} source`);
5766
modelCatalogSettings.findEnableToggle(testData.redhatAiSourceId).click({ force: true });
5867

68+
cy.step('Verify first source is disabled in configmap');
69+
verifyModelCatalogSourceEnabled(testData.redhatAiSourceId, false);
70+
5971
cy.step(`Disable the ${testData.sourceName2} source`);
6072
modelCatalogSettings.findEnableToggle(testData.redhatAiSourceId2).click({ force: true });
6173

62-
cy.step('Verify configmap shows source as disabled');
63-
verifyModelCatalogSourceEnabled(testData.redhatAiSourceId, false);
74+
cy.step(`Disable the ${testData.sourceName3} source`);
75+
modelCatalogSettings.findEnableToggle(testData.redhatAiSourceId3).click({ force: true });
76+
77+
cy.step('Verify second source is disabled in configmap');
78+
verifyModelCatalogSourceEnabled(testData.redhatAiSourceId2, false);
79+
80+
cy.step('Verify third source is disabled in configmap');
81+
verifyModelCatalogSourceEnabled(testData.redhatAiSourceId3, false);
6482

6583
cy.step('Navigate to catalog');
6684
modelCatalog.visit();
6785

68-
cy.step('Wait for model catalog to show empty state');
69-
waitForModelCatalogEmptyState();
70-
71-
cy.step('Verify model catalog shows empty state');
72-
modelCatalog.findModelCatalogEmptyState().should('exist');
86+
cy.step('Wait for catalog to reflect disabled sources');
87+
waitForModelCatalogAfterDisable([
88+
testData.redhatAiSourceId,
89+
testData.redhatAiSourceId2,
90+
testData.redhatAiSourceId3,
91+
]);
7392
},
7493
);
7594
});

packages/cypress/cypress/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,8 @@ export type ModelCatalogSourceTestData = {
611611
redhatAiSourceId: string;
612612
sourceName2: string;
613613
redhatAiSourceId2: string;
614+
sourceName3: string;
615+
redhatAiSourceId3: string;
614616
};
615617

616618
export type TrainJobTestData = {

packages/cypress/cypress/utils/oc_commands/modelCatalog.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as yaml from 'js-yaml';
12
import { execWithOutput } from './baseCommands';
23
import { getModelRegistryNamespace } from './modelRegistry';
34
import type { CommandLineResult } from '../../types';
@@ -421,6 +422,69 @@ export const waitForModelCatalogCards = (
421422
return cy.then(() => checkForCards(1));
422423
};
423424

425+
/**
426+
* Check whether any catalog sources other than the specified ones are enabled.
427+
* Reads the model-catalog-sources (user overrides) ConfigMap, falling back to
428+
* model-catalog-default-sources. Parses YAML in TypeScript via js-yaml so there
429+
* is no dependency on yq, python3 or jq being available in CI.
430+
* Sources without an explicit `.enabled` field are treated as enabled by default.
431+
* @param excludeSourceIds Source IDs to exclude from the check
432+
* @returns A Cypress chainable that resolves with true if at least one other source is enabled.
433+
*/
434+
export const hasOtherEnabledCatalogSources = (
435+
excludeSourceIds: string[],
436+
): Cypress.Chainable<boolean> => {
437+
const namespace = getModelRegistryNamespace();
438+
439+
const parseAndCount = (yamlContent: string): boolean => {
440+
const parsed = yaml.load(yamlContent) as {
441+
catalogs: Array<{ id: string; enabled?: boolean }>;
442+
};
443+
const otherEnabled = parsed.catalogs.filter(
444+
(c) => c.enabled !== false && !excludeSourceIds.includes(c.id),
445+
);
446+
cy.log(`Other enabled sources found: ${otherEnabled.length}`);
447+
return otherEnabled.length > 0;
448+
};
449+
450+
const userCmd = `oc get configmap model-catalog-sources -n ${namespace} -o jsonpath='{.data.sources\\.yaml}'`;
451+
const defaultCmd = `oc get configmap model-catalog-default-sources -n ${namespace} -o jsonpath='{.data.sources\\.yaml}'`;
452+
453+
return execWithOutput(userCmd, 30).then((userResult: CommandLineResult) => {
454+
if (userResult.code === 0 && userResult.stdout.trim()) {
455+
return cy.wrap(parseAndCount(userResult.stdout));
456+
}
457+
return execWithOutput(defaultCmd, 30).then((defaultResult: CommandLineResult) => {
458+
if (defaultResult.code !== 0 || !defaultResult.stdout.trim()) {
459+
const maskedStderr = maskSensitiveInfo(
460+
[userResult.stderr, defaultResult.stderr].filter(Boolean).join('\n'),
461+
);
462+
throw new Error(`Failed to read catalog sources configmaps: ${maskedStderr}`);
463+
}
464+
return cy.wrap(parseAndCount(defaultResult.stdout));
465+
});
466+
});
467+
};
468+
469+
/**
470+
* Poll until the model catalog UI has stabilized after disabling sources.
471+
* If other sources are still enabled, waits for cards to be present (no empty state).
472+
* If no other sources remain, waits for the empty state to appear.
473+
* @param disabledSourceIds The source IDs that were just disabled
474+
* @returns A Cypress chainable that resolves when the UI has stabilized.
475+
*/
476+
export const waitForModelCatalogAfterDisable = (
477+
disabledSourceIds: string[],
478+
): Cypress.Chainable<undefined> =>
479+
hasOtherEnabledCatalogSources(disabledSourceIds).then((hasOthers) => {
480+
if (hasOthers) {
481+
cy.step('Other catalog sources are enabled — waiting for catalog cards');
482+
return waitForModelCatalogCards();
483+
}
484+
cy.step('No other catalog sources — waiting for empty state');
485+
return waitForModelCatalogEmptyState();
486+
});
487+
424488
/**
425489
* Poll until at least one model catalog card with validated performance data is visible,
426490
* reloading the page between attempts.

0 commit comments

Comments
 (0)