Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,83 @@ import { settingClick } from '../../utils/sidebar';
// use the admin user to login
test.use({ storageState: 'playwright/.auth/admin.json' });

/**
* Installs the Search Indexing Application from the marketplace.
* Shared by the "Install application" step and the self-healing guard
* that recovers from a previous retry leaving the app uninstalled.
*/
const installSearchIndexApplication = async (page: Page) => {
const getMarketPlaceResponse = page.waitForResponse(
'/api/v1/apps/marketplace?limit=15'
);
await page.click('[data-testid="add-application"]');

const response = await getMarketPlaceResponse;

expect(response.status()).toBe(200);

// Paginate through marketplace pages until the card is found.
let cardFound = await page
.locator('[data-testid="search-indexing-application-card"]')
.isVisible();

while (!cardFound) {
const nextButton = page.locator('[data-testid="next"]');
const isNextButtonDisabled = await nextButton.isDisabled();

if (isNextButtonDisabled) {
throw new Error(
'search-indexing-application-card not found in marketplace and next button is disabled'
);
}

const nextPageResponse = page.waitForResponse('/api/v1/apps/marketplace*');
await nextButton.click();
await nextPageResponse;

cardFound = await page
.locator('[data-testid="search-indexing-application-card"]')
.isVisible();
}

await page
.getByTestId('search-indexing-application-card')
.getByTestId('config-btn')
.click();

await page.getByTestId('install-application').waitFor({ state: 'visible' });
await page.getByTestId('install-application').click();

await page.getByTestId('save-button').waitFor({ state: 'visible' });
await page.getByTestId('save-button').click();

await page.getByTestId('submit-btn').waitFor({ state: 'visible' });
await page.getByTestId('submit-btn').click();
await page.getByTestId('schedular-card-container').waitFor();
await page
.getByTestId('schedular-card-container')
.getByText('On Demand')
.click();

await expect(page.locator('[data-testid="cron-type"]')).not.toBeVisible();

const installApplicationResponse = page.waitForResponse('api/v1/apps');
const getApplications = page.waitForRequest(
(request) =>
request.url().includes('/api/v1/apps?limit') && request.method() === 'GET'
);
await page.click('[data-testid="deploy-button"]');
await installApplicationResponse;

await toastNotification(page, 'Application installed successfully');

await getApplications;

await expect(
page.getByTestId('search-indexing-application-card')
).toBeVisible();
};

const verifyLastExecutionStatus = async (page: Page) => {
const { apiContext } = await getApiContext(page);

Expand Down Expand Up @@ -77,6 +154,30 @@ test.describe('Search Index Application', PLAYWRIGHT_BASIC_TEST_TAG_OBJ, () => {

await test.step('Visit Application page', async () => {
await redirectToHomePage(page);

// If a previous retry left the app uninstalled, reinstall via API.
const { apiContext } = await getApiContext(page);
const appCheckResponse = await apiContext.get(
'/api/v1/apps/name/SearchIndexingApplication'
);

if (appCheckResponse.status() === 404) {
// appConfiguration must be passed so the Configuration tab renders in the UI.
const marketplaceResponse = await apiContext.get(
'/api/v1/apps/marketplace/name/SearchIndexingApplication'
);
const { appConfiguration } = await marketplaceResponse.json();

await apiContext.post('/api/v1/apps', {
data: {
name: 'SearchIndexingApplication',
displayName: 'Search Indexing',
appConfiguration,
appSchedule: { scheduleTimeline: 'None' },
},
});
Comment on lines +171 to +178
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Edge Case: Self-healing API POST lacks response-status check

The self-healing guard (lines 171-178) creates the app via apiContext.post('/api/v1/apps', …) but never inspects the response. If the POST fails (e.g., 400 due to a schema change or 500), the test will proceed as if the app is installed, leading to a confusing downstream failure that obscures the real cause.

Adding a status assertion gives a clear, early failure message.

Suggested fix:

const installRes = await apiContext.post('/api/v1/apps', {
  data: {
    name: 'SearchIndexingApplication',
    displayName: 'Search Indexing',
    appConfiguration,
    appSchedule: { scheduleTimeline: 'None' },
  },
});
expect(installRes.ok()).toBeTruthy();

Was this helpful? React with 👍 / 👎 | Reply gitar fix to apply this suggestion

}

await settingClick(page, GlobalSettingOptions.APPLICATIONS);
});

Expand Down Expand Up @@ -190,74 +291,7 @@ test.describe('Search Index Application', PLAYWRIGHT_BASIC_TEST_TAG_OBJ, () => {
});

await test.step('Install application', async () => {
// Verify response status code
const getMarketPlaceResponse = page.waitForResponse(
'/api/v1/apps/marketplace?limit=15'
);
await page.click('[data-testid="add-application"]');

const response = await getMarketPlaceResponse;

expect(response.status()).toBe(200);

// Check if search-indexing-application-card is visible, if not paginate through pages
let cardFound = await page
.locator('[data-testid="search-indexing-application-card"]')
.isVisible();

while (!cardFound) {
const nextButton = page.locator('[data-testid="next"]');
const isNextButtonDisabled = await nextButton.isDisabled();

if (isNextButtonDisabled) {
throw new Error(
'search-indexing-application-card not found in marketplace and next button is disabled'
);
}

// Click next page and wait for response
const nextPageResponse = page.waitForResponse(
'/api/v1/apps/marketplace*'
);
await nextButton.click();
await nextPageResponse;

// Check if card is now visible
cardFound = await page
.locator('[data-testid="search-indexing-application-card"]')
.isVisible();
}

await page.click(
'[data-testid="search-indexing-application-card"] [data-testid="config-btn"]'
);
await page.click('[data-testid="install-application"]');
await page.click('[data-testid="save-button"]');
await page.click('[data-testid="submit-btn"]');
await page.getByTestId('schedular-card-container').waitFor();
await page
.getByTestId('schedular-card-container')
.getByText('On Demand')
.click();

await expect(page.locator('[data-testid="cron-type"]')).not.toBeVisible();

const installApplicationResponse = page.waitForResponse('api/v1/apps');
const getApplications = page.waitForRequest(
(request) =>
request.url().includes('/api/v1/apps?limit') &&
request.method() === 'GET'
);
await page.click('[data-testid="deploy-button"]');
await installApplicationResponse;

await toastNotification(page, 'Application installed successfully');

await getApplications;

await expect(
page.getByTestId('search-indexing-application-card')
).toBeVisible();
await installSearchIndexApplication(page);
});

if (process.env.PLAYWRIGHT_IS_OSS) {
Expand Down
Loading