@iblai/iblai-js 1.4.2 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,18 @@
1
1
  import { Locator, Page } from '@playwright/test';
2
2
  export declare function inviteUserTest(page: Page, inviteModal: Locator): Promise<void>;
3
3
  export declare function navigateToAccountComponent(page: Page, profileBtn: Locator): Promise<Locator>;
4
+ /**
5
+ * Click a report card's primary action and confirm the date-range picker, so callers
6
+ * always end up in the post-generation state.
7
+ *
8
+ * Prefers the "Regenerate report" button when present (completed reports) because the plain
9
+ * "Download report" button on a completed card re-downloads the existing URL and skips the
10
+ * picker. When no Regenerate button exists, clicks "Download report" — which on a
11
+ * non-completed card also opens the picker.
12
+ *
13
+ * Either way we wait for the picker and click Generate/Regenerate to start the report.
14
+ */
15
+ export declare function triggerReportFromCard(page: Page, card: Locator): Promise<void>;
4
16
  export declare function navigateToDataReports(page: Page): Promise<void>;
5
17
  export declare function shouldDisplayReportCards(page: Page, REPORT_CARDS: {
6
18
  name: string;
@@ -825,6 +825,7 @@ async function expectNoAccessibilityViolationsOnDialogs(page, rules, exclude = [
825
825
  test$1.expect(results.violations).toEqual([]);
826
826
  }
827
827
 
828
+ const deferredNotificationText = 'You will be notified once the report is available.';
828
829
  async function inviteUserTest(page, inviteModal) {
829
830
  const emailInput = inviteModal.locator('#email-invite');
830
831
  await test$1.expect(emailInput).toBeVisible({ timeout: 10000 });
@@ -848,6 +849,28 @@ async function navigateToAccountComponent(page, profileBtn) {
848
849
  await test$1.expect(tenantDialog).toBeVisible();
849
850
  return tenantDialog;
850
851
  }
852
+ /**
853
+ * Click a report card's primary action and confirm the date-range picker, so callers
854
+ * always end up in the post-generation state.
855
+ *
856
+ * Prefers the "Regenerate report" button when present (completed reports) because the plain
857
+ * "Download report" button on a completed card re-downloads the existing URL and skips the
858
+ * picker. When no Regenerate button exists, clicks "Download report" — which on a
859
+ * non-completed card also opens the picker.
860
+ *
861
+ * Either way we wait for the picker and click Generate/Regenerate to start the report.
862
+ */
863
+ async function triggerReportFromCard(page, card) {
864
+ const regenerateButton = card.getByRole('button', { name: 'Regenerate report' });
865
+ const downloadButton = card.getByRole('button', { name: 'Download report' });
866
+ const hasRegenerate = await regenerateButton.isVisible().catch(() => false);
867
+ const trigger = hasRegenerate ? regenerateButton : downloadButton;
868
+ await trigger.click();
869
+ const dateRangePopover = page.getByTestId('report-date-range-popover');
870
+ await test$1.expect(dateRangePopover).toBeVisible({ timeout: 10000 });
871
+ await dateRangePopover.getByRole('button', { name: /^(Generate|Regenerate)$/ }).click();
872
+ await test$1.expect(dateRangePopover).not.toBeVisible({ timeout: 10000 });
873
+ }
851
874
  async function navigateToDataReports(page) {
852
875
  const dataReportsTab = page.getByRole('tab', { name: 'Data Reports' });
853
876
  await test$1.expect(dataReportsTab).toBeVisible({ timeout: 120000 });
@@ -899,14 +922,40 @@ async function shouldOpenCSVEditorDialog(page) {
899
922
  test$1.skip();
900
923
  return;
901
924
  }
902
- const downloadButton = userReportCard.getByRole('button', {
903
- name: 'Download report',
904
- });
905
- await downloadButton.click();
906
- const csvEditorDialog = page.getByRole('dialog', {
907
- name: 'Edit CSV Data',
908
- });
909
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
925
+ await triggerReportFromCard(page, userReportCard);
926
+ //expect Report generation processing...
927
+ await test$1.expect(page.getByText('Report generation processing...')).toBeVisible({ timeout: 5000 });
928
+ // After the processing toast appears, the backend either finishes in time (CSV editor opens)
929
+ // or polling times out (deferred-notification toast appears). Treat both as valid outcomes.
930
+ await waitForReportOutcomeOrCSVDialogOpen(page);
931
+ }
932
+ async function waitForReportOutcomeOrCSVDialogOpen(page) {
933
+ const csvEditorDialog = page.getByRole('dialog', { name: 'Edit CSV Data' });
934
+ const deferredNotification = page.getByText(deferredNotificationText);
935
+ const downloadPromise = page.waitForEvent('download', { timeout: 60000 });
936
+ const outcome = await Promise.race([
937
+ csvEditorDialog
938
+ .waitFor({ state: 'visible', timeout: 60000 })
939
+ .then(() => 'csv-editor'),
940
+ deferredNotification
941
+ .waitFor({ state: 'visible', timeout: 60000 })
942
+ .then(() => 'deferred'),
943
+ downloadPromise.then(() => 'download'),
944
+ ]).catch(() => null);
945
+ if (outcome === 'deferred') {
946
+ logger.info('Report generation deferred — user will be notified when available');
947
+ return false;
948
+ }
949
+ if (outcome === 'download') {
950
+ const download = await downloadPromise;
951
+ const filename = download.suggestedFilename();
952
+ test$1.expect(filename).toBe('report.csv');
953
+ logger.info(`CSV file "${filename}" downloaded successfully`);
954
+ return false;
955
+ }
956
+ if (outcome !== 'csv-editor') {
957
+ throw new Error('Report generation did not complete: neither CSV editor nor deferred-notification toast appeared within 60s');
958
+ }
910
959
  const dialogTitle = csvEditorDialog.getByRole('heading', {
911
960
  name: 'Edit CSV Data',
912
961
  level: 2,
@@ -920,6 +969,7 @@ async function shouldOpenCSVEditorDialog(page) {
920
969
  await test$1.expect(csvEditorDialog.getByRole('button', { name: 'Save' })).toBeVisible();
921
970
  await test$1.expect(csvEditorDialog.getByRole('button', { name: 'Close' }).first()).toBeVisible();
922
971
  logger.info('CSV Editor dialog opened successfully');
972
+ return csvEditorDialog;
923
973
  }
924
974
  async function shouldDisplayCSVInEditableTableFormat(page) {
925
975
  const dataReportsTab = page.getByRole('tab', { name: 'Data Reports' });
@@ -932,14 +982,12 @@ async function shouldDisplayCSVInEditableTableFormat(page) {
932
982
  test$1.skip();
933
983
  return;
934
984
  }
935
- const downloadButton = userReportCard.getByRole('button', {
936
- name: 'Download report',
937
- });
938
- await downloadButton.click();
939
- const csvEditorDialog = page.getByRole('dialog', {
940
- name: 'Edit CSV Data',
941
- });
942
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
985
+ await triggerReportFromCard(page, userReportCard);
986
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
987
+ if (!csvEditorDialog) {
988
+ test$1.skip();
989
+ return;
990
+ }
943
991
  const table = csvEditorDialog.getByRole('table', {
944
992
  name: 'CSV data table',
945
993
  });
@@ -963,14 +1011,12 @@ async function shouldAllowEditingCellValuesInCSVEditor(page) {
963
1011
  test$1.skip();
964
1012
  return;
965
1013
  }
966
- const downloadButton = userReportCard.getByRole('button', {
967
- name: 'Download report',
968
- });
969
- await downloadButton.click();
970
- const csvEditorDialog = page.getByRole('dialog', {
971
- name: 'Edit CSV Data',
972
- });
973
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
1014
+ await triggerReportFromCard(page, userReportCard);
1015
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
1016
+ if (!csvEditorDialog) {
1017
+ test$1.skip();
1018
+ return;
1019
+ }
974
1020
  const table = csvEditorDialog.getByRole('table', {
975
1021
  name: 'CSV data table',
976
1022
  });
@@ -1003,14 +1049,12 @@ async function shouldAddNewRowWhenClickingAddRowButton(page) {
1003
1049
  test$1.skip();
1004
1050
  return;
1005
1051
  }
1006
- const downloadButton = userReportCard.getByRole('button', {
1007
- name: 'Download report',
1008
- });
1009
- await downloadButton.click();
1010
- const csvEditorDialog = page.getByRole('dialog', {
1011
- name: 'Edit CSV Data',
1012
- });
1013
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
1052
+ await triggerReportFromCard(page, userReportCard);
1053
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
1054
+ if (!csvEditorDialog) {
1055
+ test$1.skip();
1056
+ return;
1057
+ }
1014
1058
  const table = csvEditorDialog.locator('table');
1015
1059
  const initialRowCount = await table.locator('tbody tr').count();
1016
1060
  const addRowButton = csvEditorDialog.getByRole('button', {
@@ -1033,16 +1077,14 @@ async function shouldSaveEditedCSVAndTriggerDownload(page) {
1033
1077
  test$1.skip();
1034
1078
  return;
1035
1079
  }
1036
- const downloadButton = userReportCard.getByRole('button', {
1037
- name: 'Download report',
1038
- });
1039
- await downloadButton.click();
1080
+ await triggerReportFromCard(page, userReportCard);
1040
1081
  await page.waitForLoadState('networkidle');
1041
- const csvEditorDialog = page.getByRole('dialog', {
1042
- name: 'Edit CSV Data',
1043
- });
1044
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
1045
- const downloadPromise = page.waitForEvent('download', { timeout: 30000 });
1082
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
1083
+ if (!csvEditorDialog) {
1084
+ test$1.skip();
1085
+ return;
1086
+ }
1087
+ const downloadPromise = page.waitForEvent('download', { timeout: 40000 });
1046
1088
  const saveButton = csvEditorDialog.getByRole('button', { name: 'Save' });
1047
1089
  await saveButton.click();
1048
1090
  const download = await downloadPromise;
@@ -1062,15 +1104,13 @@ async function shouldCloseCSVEditorWithoutSavingWhenClickingCancel(page) {
1062
1104
  test$1.skip();
1063
1105
  return;
1064
1106
  }
1065
- const downloadButton = userReportCard.getByRole('button', {
1066
- name: 'Download report',
1067
- });
1068
- await downloadButton.click();
1107
+ await triggerReportFromCard(page, userReportCard);
1069
1108
  await page.waitForLoadState('networkidle');
1070
- const csvEditorDialog = page.getByRole('dialog', {
1071
- name: 'Edit CSV Data',
1072
- });
1073
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
1109
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
1110
+ if (!csvEditorDialog) {
1111
+ test$1.skip();
1112
+ return;
1113
+ }
1074
1114
  const cancelButton = csvEditorDialog.getByRole('button', {
1075
1115
  name: 'Cancel',
1076
1116
  });
@@ -1090,15 +1130,13 @@ async function shouldCloseCSVEditorWhenClickingCloseButton(page) {
1090
1130
  test$1.skip();
1091
1131
  return;
1092
1132
  }
1093
- const downloadButton = userReportCard.getByRole('button', {
1094
- name: 'Download report',
1095
- });
1096
- await downloadButton.click();
1133
+ await triggerReportFromCard(page, userReportCard);
1097
1134
  await page.waitForLoadState('networkidle');
1098
- const csvEditorDialog = page.getByRole('dialog', {
1099
- name: 'Edit CSV Data',
1100
- });
1101
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
1135
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
1136
+ if (!csvEditorDialog) {
1137
+ test$1.skip();
1138
+ return;
1139
+ }
1102
1140
  const closeButton = csvEditorDialog
1103
1141
  .getByRole('button', {
1104
1142
  name: 'Close',
@@ -1119,15 +1157,13 @@ async function shouldVerifyCSVEditorDialogAccessibility(page) {
1119
1157
  test$1.skip();
1120
1158
  return;
1121
1159
  }
1122
- const downloadButton = userReportCard.getByRole('button', {
1123
- name: 'Download report',
1124
- });
1125
- await downloadButton.click();
1160
+ await triggerReportFromCard(page, userReportCard);
1126
1161
  await page.waitForLoadState('networkidle');
1127
- const csvEditorDialog = page.getByRole('dialog', {
1128
- name: 'Edit CSV Data',
1129
- });
1130
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
1162
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
1163
+ if (!csvEditorDialog) {
1164
+ test$1.skip();
1165
+ return;
1166
+ }
1131
1167
  await test$1.expect(csvEditorDialog).toHaveAttribute('role', 'dialog');
1132
1168
  const csvTable = csvEditorDialog.getByRole('table', {
1133
1169
  name: 'CSV data table',
@@ -1162,15 +1198,12 @@ async function shouldOpenCSVEditorForUserMetadataReport(page) {
1162
1198
  test$1.skip();
1163
1199
  return;
1164
1200
  }
1165
- const downloadButton = userMetadataReportCard.getByRole('button', {
1166
- name: 'Download report',
1167
- });
1168
- await downloadButton.click();
1169
- await page.waitForLoadState('networkidle');
1170
- const csvEditorDialog = page.getByRole('dialog', {
1171
- name: 'Edit CSV Data',
1172
- });
1173
- await test$1.expect(csvEditorDialog).toBeVisible({ timeout: 60000 });
1201
+ await triggerReportFromCard(page, userMetadataReportCard);
1202
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
1203
+ if (!csvEditorDialog) {
1204
+ test$1.skip();
1205
+ return;
1206
+ }
1174
1207
  const table = csvEditorDialog.locator('table');
1175
1208
  const companyHeader = table.locator('thead th input[value="company"]');
1176
1209
  const hasCompanyColumn = await companyHeader.isVisible().catch(() => false);
@@ -1190,21 +1223,32 @@ async function shouldDirectlyDownloadChatHistoryReportWithoutCSVEditor(page) {
1190
1223
  test$1.skip();
1191
1224
  return;
1192
1225
  }
1193
- const downloadButton = chatHistoryCard.getByRole('button', {
1194
- name: 'Download report',
1195
- });
1196
- const downloadPromise = page.waitForEvent('download', { timeout: 120000 });
1197
- await test$1.expect(downloadButton).toBeVisible({ timeout: 30000 });
1198
- await downloadButton.click();
1199
- try {
1200
- const download = await downloadPromise;
1201
- const filename = download.suggestedFilename();
1226
+ // Prefer the Regenerate button (guaranteed to open the picker); fall back to Download.
1227
+ // Start listening for the download event before clicking since the auto-download fires
1228
+ // as soon as the picker is confirmed.
1229
+ const downloadPromise = page.waitForEvent('download', { timeout: 40000 });
1230
+ await triggerReportFromCard(page, chatHistoryCard);
1231
+ // Either the browser fires a download within 40s, or the backend defers and shows the
1232
+ // "You will be notified once the report is available." toast. Both are valid outcomes.
1233
+ const deferredNotification = page.getByText(deferredNotificationText);
1234
+ const outcome = await Promise.race([
1235
+ downloadPromise.then((download) => ({ kind: 'download', download })),
1236
+ deferredNotification
1237
+ .waitFor({ state: 'visible', timeout: 40000 })
1238
+ .then(() => ({ kind: 'deferred' })),
1239
+ ]).catch(() => null);
1240
+ if (!outcome) {
1241
+ throw new Error('Chat History generation did not complete: neither a download nor the deferred-notification toast appeared within 40s');
1242
+ }
1243
+ if (outcome.kind === 'download') {
1244
+ const filename = outcome.download.suggestedFilename();
1202
1245
  test$1.expect(filename.endsWith('.csv')).toBeTruthy();
1203
1246
  logger.info(`Chat History report downloaded: ${filename}`);
1204
1247
  }
1205
- catch (_a) {
1206
- logger.info('Chat History download may have completed without triggering download event');
1248
+ else {
1249
+ logger.info('Chat History generation deferred user will be notified when available');
1207
1250
  }
1251
+ // Regardless of outcome, the CSV editor dialog should never have opened for chat history.
1208
1252
  const csvEditorDialog = page.getByRole('dialog', {
1209
1253
  name: 'Edit CSV Data',
1210
1254
  });
@@ -1225,23 +1269,23 @@ async function shouldDisableOtherDownloadButtonsWhileGeneratingReport(page) {
1225
1269
  test$1.skip();
1226
1270
  return;
1227
1271
  }
1228
- await downloadButtons.first().click();
1272
+ // Trigger generation on the first report card (preferring Regenerate when present so the
1273
+ // date-range picker always shows, then confirming it).
1274
+ const firstCard = page.getByLabel(/ report card$/).first();
1275
+ await triggerReportFromCard(page, firstCard);
1276
+ //slight timeout to wait for other download buttons to be disabled
1229
1277
  await page.waitForTimeout(500);
1230
1278
  const secondButton = downloadButtons.nth(1);
1231
1279
  const isSecondDisabled = await secondButton.isDisabled();
1232
1280
  if (isSecondDisabled) {
1233
1281
  logger.info('Other download buttons correctly disabled during report generation');
1234
1282
  }
1235
- const csvEditorDialog = page.getByRole('dialog', {
1236
- name: 'Edit CSV Data',
1237
- });
1238
- const dialogVisible = await csvEditorDialog
1239
- .waitFor({ state: 'visible', timeout: 30000 })
1240
- .then(() => true)
1241
- .catch(() => false);
1242
- if (dialogVisible) {
1243
- await csvEditorDialog.getByRole('button', { name: 'Close' }).first().click();
1283
+ const csvEditorDialog = await waitForReportOutcomeOrCSVDialogOpen(page);
1284
+ if (!csvEditorDialog) {
1285
+ test$1.skip();
1286
+ return;
1244
1287
  }
1288
+ await csvEditorDialog.getByRole('button', { name: 'Close' }).first().click();
1245
1289
  }
1246
1290
  // ============================================
1247
1291
  // Combined Recommendation Reports Test Helpers
@@ -1261,12 +1305,9 @@ async function shouldShowCombiningReportsDialog(page) {
1261
1305
  test$1.skip();
1262
1306
  return;
1263
1307
  }
1264
- const downloadButton = combinedReportCard
1265
- .first()
1266
- .getByRole('button', { name: 'Download report' });
1267
- await downloadButton.click();
1308
+ await triggerReportFromCard(page, combinedReportCard.first());
1268
1309
  const combiningDialog = page.locator('[data-testid="combining-reports-dialog"]');
1269
- await test$1.expect(combiningDialog).toBeVisible({ timeout: 30000 });
1310
+ await test$1.expect(combiningDialog).toBeVisible({ timeout: 40000 });
1270
1311
  await test$1.expect(combiningDialog.getByText('Combining Reports')).toBeVisible();
1271
1312
  await test$1.expect(combiningDialog.getByText('Loading & combining recommendations data')).toBeVisible();
1272
1313
  logger.info('Combining reports dialog displayed successfully');
@@ -1286,12 +1327,9 @@ async function shouldCancelCombiningReports(page) {
1286
1327
  test$1.skip();
1287
1328
  return;
1288
1329
  }
1289
- const downloadButton = combinedReportCard
1290
- .first()
1291
- .getByRole('button', { name: 'Download report' });
1292
- await downloadButton.click();
1330
+ await triggerReportFromCard(page, combinedReportCard.first());
1293
1331
  const combiningDialog = page.locator('[data-testid="combining-reports-dialog"]');
1294
- await test$1.expect(combiningDialog).toBeVisible({ timeout: 30000 });
1332
+ await test$1.expect(combiningDialog).toBeVisible({ timeout: 40000 });
1295
1333
  const cancelButton = page.locator('[data-testid="cancel-combining-button"]');
1296
1334
  await test$1.expect(cancelButton).toBeVisible();
1297
1335
  await cancelButton.click();
@@ -1332,10 +1370,7 @@ async function shouldCombineRecommendationReports(page) {
1332
1370
  test$1.skip();
1333
1371
  return;
1334
1372
  }
1335
- const downloadButton = combinedReportCard
1336
- .first()
1337
- .getByRole('button', { name: 'Download report' });
1338
- await downloadButton.click();
1373
+ await triggerReportFromCard(page, combinedReportCard.first());
1339
1374
  const combiningDialog = page.locator('[data-testid="combining-reports-dialog"]');
1340
1375
  const dialogOrEditor = await Promise.race([
1341
1376
  combiningDialog.waitFor({ state: 'visible', timeout: 10000 }).then(() => 'dialog'),