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