@iblai/iblai-js 2.4.3 → 2.4.5
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/agent-skills-helpers.d.ts +23 -10
- package/dist/data-layer/playwright/index.d.ts +3 -1
- package/dist/data-layer/playwright/spend-limits-helpers.d.ts +275 -0
- package/dist/playwright/index.cjs +770 -66
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.d.ts +300 -12
- package/dist/playwright/index.esm.js +737 -67
- package/dist/playwright/index.esm.js.map +1 -1
- package/dist/playwright/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/playwright/playwright/index.d.ts +3 -1
- package/dist/playwright/playwright/spend-limits-helpers.d.ts +275 -0
- package/dist/security/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/security/playwright/index.d.ts +3 -1
- package/dist/security/playwright/spend-limits-helpers.d.ts +275 -0
- package/dist/web-containers/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/web-containers/playwright/index.d.ts +3 -1
- package/dist/web-containers/playwright/spend-limits-helpers.d.ts +275 -0
- package/dist/web-containers/source/index.esm.js +9278 -6500
- package/dist/web-containers/source/next/index.esm.js +2308 -245
- package/dist/web-utils/playwright/agent-skills-helpers.d.ts +23 -10
- package/dist/web-utils/playwright/index.d.ts +3 -1
- package/dist/web-utils/playwright/spend-limits-helpers.d.ts +275 -0
- package/package.json +5 -5
|
@@ -2328,8 +2328,9 @@ async function verifySkillsTabVisible(page) {
|
|
|
2328
2328
|
logger.info('Skills section is visible with Agent/Available sub-tabs');
|
|
2329
2329
|
}
|
|
2330
2330
|
/**
|
|
2331
|
-
* Switch to the "Agent Skills" sub-tab (the agent's
|
|
2332
|
-
* toggles; the default sub-tab) and
|
|
2331
|
+
* Switch to the "Agent Skills" sub-tab (the agent's assigned skills with
|
|
2332
|
+
* enable toggles, paged server-side 10 per page; the default sub-tab) and
|
|
2333
|
+
* wait for its panel to render.
|
|
2333
2334
|
*/
|
|
2334
2335
|
async function switchToAgentSkillsSubTab(page) {
|
|
2335
2336
|
await page.getByTestId('agent-skills-tab-agent').click();
|
|
@@ -2415,8 +2416,8 @@ async function addSkillToAgent(page, skillName) {
|
|
|
2415
2416
|
logger.info(`Added skill "${skillName}" to the agent`);
|
|
2416
2417
|
}
|
|
2417
2418
|
/**
|
|
2418
|
-
* Verify a catalog row shows the "Added" chip (
|
|
2419
|
-
*
|
|
2419
|
+
* Verify a catalog row shows the "Added" chip (the skill is assigned to the
|
|
2420
|
+
* agent) and offers no Add button.
|
|
2420
2421
|
*/
|
|
2421
2422
|
async function verifySkillAdded(page, skillName) {
|
|
2422
2423
|
const row = availableSkillRow(page, skillName);
|
|
@@ -2425,8 +2426,10 @@ async function verifySkillAdded(page, skillName) {
|
|
|
2425
2426
|
logger.info(`Skill "${skillName}" shows as Added`);
|
|
2426
2427
|
}
|
|
2427
2428
|
/**
|
|
2428
|
-
* Open the three-dots actions menu for a skill row
|
|
2429
|
-
*
|
|
2429
|
+
* Open the three-dots actions menu for a skill row on the active sub-tab.
|
|
2430
|
+
* Agent-tab rows offer "Remove from Agent" only; catalog rows offer
|
|
2431
|
+
* Edit/Delete (for editable skills). The menu content portals to
|
|
2432
|
+
* document.body, so the returned locator is page-level by design.
|
|
2430
2433
|
*/
|
|
2431
2434
|
async function openSkillActionsMenu(page, skillName) {
|
|
2432
2435
|
const actionsButton = page.getByRole('button', { name: `${skillName} actions` });
|
|
@@ -2502,6 +2505,23 @@ async function toggleSkill(page, skillName) {
|
|
|
2502
2505
|
await enableSkill(page, skillName);
|
|
2503
2506
|
return true;
|
|
2504
2507
|
}
|
|
2508
|
+
/**
|
|
2509
|
+
* Set a skill's platform-wide enabled flag via its "Available Skills"
|
|
2510
|
+
* catalog row Switch (skill-level PATCH — the agent-tab switches PATCH the
|
|
2511
|
+
* assignment instead). No-op when already in the requested state; waits for
|
|
2512
|
+
* the success toast otherwise. Read-only featured rows have no switch.
|
|
2513
|
+
*/
|
|
2514
|
+
async function setCatalogSkillEnabled(page, skillName, enabled) {
|
|
2515
|
+
const switchEl = availableSkillRow(page, skillName).getByRole('switch');
|
|
2516
|
+
await expect(switchEl).toBeVisible({ timeout: 10000 });
|
|
2517
|
+
if ((await switchEl.isChecked()) === enabled) {
|
|
2518
|
+
logger.info(`Catalog skill "${skillName}" already ${enabled ? 'enabled' : 'disabled'}`);
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
await switchEl.click();
|
|
2522
|
+
await expect(page.getByText(`${skillName} ${enabled ? 'enabled' : 'disabled'}`, { exact: false })).toBeVisible({ timeout: 10000 });
|
|
2523
|
+
logger.info(`${enabled ? 'Enabled' : 'Disabled'} catalog skill "${skillName}"`);
|
|
2524
|
+
}
|
|
2505
2525
|
/**
|
|
2506
2526
|
* Open the "New Skill" dialog from the Skills section. Returns the dialog
|
|
2507
2527
|
* locator — resolve every field from it (the dialog stacks over the
|
|
@@ -2572,7 +2592,9 @@ async function createSkill(page, values) {
|
|
|
2572
2592
|
logger.info(`Created skill "${values.name}"`);
|
|
2573
2593
|
}
|
|
2574
2594
|
/**
|
|
2575
|
-
* Open the Edit Skill dialog for a row (lands on the General
|
|
2595
|
+
* Open the Edit Skill dialog for a catalog row (lands on the General
|
|
2596
|
+
* sub-tab). Skill edit/delete actions live on the "Available Skills"
|
|
2597
|
+
* sub-tab — switch there first (`switchToAvailableSkillsSubTab`).
|
|
2576
2598
|
* Returns the dialog locator — resolve fields and sub-tabs from it.
|
|
2577
2599
|
*/
|
|
2578
2600
|
async function openEditSkillDialog(page, skillName) {
|
|
@@ -2582,7 +2604,8 @@ async function openEditSkillDialog(page, skillName) {
|
|
|
2582
2604
|
return waitForDialogReady(page, dialog);
|
|
2583
2605
|
}
|
|
2584
2606
|
/**
|
|
2585
|
-
* Edit an existing platform-level skill (General sub-tab fields)
|
|
2607
|
+
* Edit an existing platform-level skill (General sub-tab fields) from its
|
|
2608
|
+
* catalog row — switch to the "Available Skills" sub-tab first.
|
|
2586
2609
|
*/
|
|
2587
2610
|
async function editSkill(page, skillName, updates) {
|
|
2588
2611
|
const dialog = await openEditSkillDialog(page, skillName);
|
|
@@ -2620,8 +2643,8 @@ async function editSkill(page, skillName, updates) {
|
|
|
2620
2643
|
logger.info(`Edited skill "${skillName}"`);
|
|
2621
2644
|
}
|
|
2622
2645
|
/**
|
|
2623
|
-
* Delete a platform-level skill via its row menu
|
|
2624
|
-
* "Delete Skill" dialog.
|
|
2646
|
+
* Delete a platform-level skill via its catalog row menu (switch to the
|
|
2647
|
+
* "Available Skills" sub-tab first), confirming the "Delete Skill" dialog.
|
|
2625
2648
|
*/
|
|
2626
2649
|
async function deleteSkill(page, skillName) {
|
|
2627
2650
|
const menu = await openSkillActionsMenu(page, skillName);
|
|
@@ -4358,19 +4381,19 @@ const GRADER_LABELS = {
|
|
|
4358
4381
|
both: 'Overall + per Criterion',
|
|
4359
4382
|
},
|
|
4360
4383
|
};
|
|
4361
|
-
const UI_TIMEOUT = 10000;
|
|
4362
|
-
const MUTATION_TIMEOUT = 15000;
|
|
4384
|
+
const UI_TIMEOUT$1 = 10000;
|
|
4385
|
+
const MUTATION_TIMEOUT$1 = 15000;
|
|
4363
4386
|
/**
|
|
4364
4387
|
* The edit-agent dialog that hosts the settings tabs. Scoping through the
|
|
4365
4388
|
* dialog first keeps every subsequent query away from same-named elements
|
|
4366
4389
|
* elsewhere on the page (nested portals, background page content).
|
|
4367
4390
|
*/
|
|
4368
|
-
function editAgentDialog(page) {
|
|
4391
|
+
function editAgentDialog$1(page) {
|
|
4369
4392
|
return page.getByRole('dialog').filter({ has: page.getByRole('tablist') });
|
|
4370
4393
|
}
|
|
4371
4394
|
/** The Grader tab's body, scoped through the edit-agent dialog. */
|
|
4372
4395
|
function graderTabBody(page) {
|
|
4373
|
-
return editAgentDialog(page).getByTestId('grader-tab-body');
|
|
4396
|
+
return editAgentDialog$1(page).getByTestId('grader-tab-body');
|
|
4374
4397
|
}
|
|
4375
4398
|
/** The Rubric sub-tab's section within the tab body. */
|
|
4376
4399
|
function criteriaSection(page) {
|
|
@@ -4387,7 +4410,7 @@ function criterionRow(page, name) {
|
|
|
4387
4410
|
* edit-agent dialog (host didn't register it, or RBAC hid it).
|
|
4388
4411
|
*/
|
|
4389
4412
|
async function isGraderTabVisible(page) {
|
|
4390
|
-
const tab = editAgentDialog(page).getByRole('tab', {
|
|
4413
|
+
const tab = editAgentDialog$1(page).getByRole('tab', {
|
|
4391
4414
|
name: GRADER_LABELS.tabName,
|
|
4392
4415
|
exact: true,
|
|
4393
4416
|
});
|
|
@@ -4404,11 +4427,11 @@ async function isGraderTabVisible(page) {
|
|
|
4404
4427
|
* open. Completion is gated on the tab body's testid, not on timing.
|
|
4405
4428
|
*/
|
|
4406
4429
|
async function switchToGraderTab(page) {
|
|
4407
|
-
const dialog = editAgentDialog(page);
|
|
4430
|
+
const dialog = editAgentDialog$1(page);
|
|
4408
4431
|
const tab = dialog.getByRole('tab', { name: GRADER_LABELS.tabName, exact: true });
|
|
4409
|
-
await expect(tab).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4432
|
+
await expect(tab).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4410
4433
|
await tab.click();
|
|
4411
|
-
await expect(graderTabBody(page)).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4434
|
+
await expect(graderTabBody(page)).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4412
4435
|
logger.info('Switched to Grader tab');
|
|
4413
4436
|
}
|
|
4414
4437
|
/**
|
|
@@ -4418,7 +4441,7 @@ async function switchToGraderTab(page) {
|
|
|
4418
4441
|
async function switchToGraderSubTab(page, subTab) {
|
|
4419
4442
|
const body = graderTabBody(page);
|
|
4420
4443
|
const trigger = body.getByTestId(`grader-sub-tab-${subTab}`);
|
|
4421
|
-
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4444
|
+
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4422
4445
|
await trigger.click();
|
|
4423
4446
|
const sectionTestId = {
|
|
4424
4447
|
setup: 'grader-setup-section',
|
|
@@ -4426,13 +4449,13 @@ async function switchToGraderSubTab(page, subTab) {
|
|
|
4426
4449
|
results: 'grader-results-section',
|
|
4427
4450
|
}[subTab];
|
|
4428
4451
|
const section = body.getByTestId(sectionTestId);
|
|
4429
|
-
await expect(section).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4452
|
+
await expect(section).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4430
4453
|
logger.info(`Switched to Grader ${subTab} sub-tab`);
|
|
4431
4454
|
}
|
|
4432
4455
|
/** Read the current on/off state of the Grading capability toggle. */
|
|
4433
4456
|
async function isGradingEnabled(page) {
|
|
4434
4457
|
const toggle = graderTabBody(page).getByTestId('grader-capability-toggle');
|
|
4435
|
-
await expect(toggle).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4458
|
+
await expect(toggle).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4436
4459
|
return (await toggle.getAttribute('aria-checked')) === 'true';
|
|
4437
4460
|
}
|
|
4438
4461
|
/**
|
|
@@ -4444,35 +4467,35 @@ async function isGradingEnabled(page) {
|
|
|
4444
4467
|
async function setGradingEnabled(page, enabled) {
|
|
4445
4468
|
const body = graderTabBody(page);
|
|
4446
4469
|
const toggle = body.getByTestId('grader-capability-toggle');
|
|
4447
|
-
await expect(toggle).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4470
|
+
await expect(toggle).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4448
4471
|
if ((await toggle.getAttribute('aria-checked')) === String(enabled)) {
|
|
4449
4472
|
logger.info(`Grading already ${enabled ? 'enabled' : 'disabled'} — no toggle needed`);
|
|
4450
4473
|
return;
|
|
4451
4474
|
}
|
|
4452
|
-
await expect(toggle).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
4475
|
+
await expect(toggle).toBeEnabled({ timeout: UI_TIMEOUT$1 });
|
|
4453
4476
|
await toggle.click();
|
|
4454
4477
|
const toastText = enabled ? GRADER_LABELS.toasts.toggleOn : GRADER_LABELS.toasts.toggleOff;
|
|
4455
4478
|
// .first() is deliberate: rapid toggles can stack identical sonner toasts,
|
|
4456
4479
|
// and any one of them proves the PATCH resolved.
|
|
4457
|
-
await expect(page.getByText(toastText).first()).toBeVisible({ timeout: MUTATION_TIMEOUT });
|
|
4480
|
+
await expect(page.getByText(toastText).first()).toBeVisible({ timeout: MUTATION_TIMEOUT$1 });
|
|
4458
4481
|
await expect(toggle).toHaveAttribute('aria-checked', String(enabled), {
|
|
4459
|
-
timeout: MUTATION_TIMEOUT,
|
|
4482
|
+
timeout: MUTATION_TIMEOUT$1,
|
|
4460
4483
|
});
|
|
4461
|
-
await expect(body.getByTestId('capability-gate-content')).toHaveAttribute('data-enabled', String(enabled), { timeout: UI_TIMEOUT });
|
|
4484
|
+
await expect(body.getByTestId('capability-gate-content')).toHaveAttribute('data-enabled', String(enabled), { timeout: UI_TIMEOUT$1 });
|
|
4462
4485
|
logger.info(`Grading ${enabled ? 'enabled' : 'disabled'}`);
|
|
4463
4486
|
}
|
|
4464
4487
|
/** Pick an option in one of the setup form's two Radix selects. */
|
|
4465
|
-
async function pickSelectOption(page, triggerTestId, optionLabel) {
|
|
4488
|
+
async function pickSelectOption$1(page, triggerTestId, optionLabel) {
|
|
4466
4489
|
const trigger = graderTabBody(page).getByTestId(triggerTestId);
|
|
4467
|
-
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4490
|
+
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4468
4491
|
await trigger.click();
|
|
4469
4492
|
// Radix renders the listbox in a portal outside the dialog, so the
|
|
4470
4493
|
// option is looked up by role at page level — the open listbox is the
|
|
4471
4494
|
// only one in the document.
|
|
4472
4495
|
const option = page.getByRole('option', { name: optionLabel, exact: true });
|
|
4473
|
-
await expect(option).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4496
|
+
await expect(option).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4474
4497
|
await option.click();
|
|
4475
|
-
await expect(option).toBeHidden({ timeout: UI_TIMEOUT });
|
|
4498
|
+
await expect(option).toBeHidden({ timeout: UI_TIMEOUT$1 });
|
|
4476
4499
|
}
|
|
4477
4500
|
/**
|
|
4478
4501
|
* Fill and persist the Grading setup form (switches to the setup sub-tab
|
|
@@ -4484,26 +4507,26 @@ async function saveGraderConfig(page, values) {
|
|
|
4484
4507
|
await switchToGraderSubTab(page, 'setup');
|
|
4485
4508
|
const body = graderTabBody(page);
|
|
4486
4509
|
if (values.gradingMode) {
|
|
4487
|
-
await pickSelectOption(page, 'grader-grading-mode-select', GRADER_LABELS.gradingModeOptions[values.gradingMode]);
|
|
4510
|
+
await pickSelectOption$1(page, 'grader-grading-mode-select', GRADER_LABELS.gradingModeOptions[values.gradingMode]);
|
|
4488
4511
|
}
|
|
4489
4512
|
if (values.feedbackMode) {
|
|
4490
|
-
await pickSelectOption(page, 'grader-feedback-mode-select', GRADER_LABELS.feedbackModeOptions[values.feedbackMode]);
|
|
4513
|
+
await pickSelectOption$1(page, 'grader-feedback-mode-select', GRADER_LABELS.feedbackModeOptions[values.feedbackMode]);
|
|
4491
4514
|
}
|
|
4492
4515
|
if (values.instructions !== undefined) {
|
|
4493
4516
|
const textarea = body.getByTestId('grader-instructions-textarea');
|
|
4494
|
-
await expect(textarea).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4517
|
+
await expect(textarea).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4495
4518
|
await textarea.fill(values.instructions);
|
|
4496
4519
|
}
|
|
4497
4520
|
const saveButton = body.getByTestId('grader-save-button');
|
|
4498
|
-
await expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
4521
|
+
await expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT$1 });
|
|
4499
4522
|
await saveButton.click();
|
|
4500
|
-
await expect(saveButton).toBeDisabled({ timeout: MUTATION_TIMEOUT });
|
|
4523
|
+
await expect(saveButton).toBeDisabled({ timeout: MUTATION_TIMEOUT$1 });
|
|
4501
4524
|
logger.info('Saved grader configuration');
|
|
4502
4525
|
}
|
|
4503
4526
|
/** Fill the criterion modal's three fields and submit it. */
|
|
4504
4527
|
async function submitCriterionModal(page, criterion) {
|
|
4505
4528
|
const modal = page.getByTestId('grader-criterion-modal');
|
|
4506
|
-
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4529
|
+
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4507
4530
|
await modal.getByLabel(GRADER_LABELS.modal.fields.name, { exact: true }).fill(criterion.name);
|
|
4508
4531
|
await modal
|
|
4509
4532
|
.getByLabel(GRADER_LABELS.modal.fields.criteria, { exact: true })
|
|
@@ -4512,21 +4535,21 @@ async function submitCriterionModal(page, criterion) {
|
|
|
4512
4535
|
.getByLabel(GRADER_LABELS.modal.fields.points, { exact: true })
|
|
4513
4536
|
.fill(String(criterion.points));
|
|
4514
4537
|
const saveButton = modal.getByTestId('grader-criterion-modal-save');
|
|
4515
|
-
await expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
4538
|
+
await expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT$1 });
|
|
4516
4539
|
await saveButton.click();
|
|
4517
4540
|
// The modal only closes itself after the mutation resolves.
|
|
4518
|
-
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
4541
|
+
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT$1 });
|
|
4519
4542
|
}
|
|
4520
4543
|
/** Open a rubric row's three-dots menu and click one of its actions. */
|
|
4521
4544
|
async function clickRowMenuAction(page, name, action) {
|
|
4522
4545
|
const trigger = criterionRow(page, name).getByRole('button', {
|
|
4523
4546
|
name: GRADER_LABELS.menu.actionsAria(name),
|
|
4524
4547
|
});
|
|
4525
|
-
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4548
|
+
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4526
4549
|
await trigger.click();
|
|
4527
4550
|
// The menu portals to the page root — the open menu is the only one.
|
|
4528
4551
|
const item = page.getByRole('menuitem', { name: action, exact: true });
|
|
4529
|
-
await expect(item).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4552
|
+
await expect(item).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4530
4553
|
await item.click();
|
|
4531
4554
|
}
|
|
4532
4555
|
/**
|
|
@@ -4538,10 +4561,10 @@ async function clickRowMenuAction(page, name, action) {
|
|
|
4538
4561
|
async function addGraderCriterion(page, criterion) {
|
|
4539
4562
|
await switchToGraderSubTab(page, 'rubric');
|
|
4540
4563
|
const addButton = criteriaSection(page).getByTestId('grader-add-criterion-button');
|
|
4541
|
-
await expect(addButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
4564
|
+
await expect(addButton).toBeEnabled({ timeout: UI_TIMEOUT$1 });
|
|
4542
4565
|
await addButton.click();
|
|
4543
4566
|
await submitCriterionModal(page, criterion);
|
|
4544
|
-
await expect(criterionRow(page, criterion.name)).toBeVisible({ timeout: MUTATION_TIMEOUT });
|
|
4567
|
+
await expect(criterionRow(page, criterion.name)).toBeVisible({ timeout: MUTATION_TIMEOUT$1 });
|
|
4545
4568
|
logger.info(`Added rubric criterion "${criterion.name}"`);
|
|
4546
4569
|
}
|
|
4547
4570
|
/**
|
|
@@ -4554,7 +4577,7 @@ async function editGraderCriterion(page, currentName, updates) {
|
|
|
4554
4577
|
await switchToGraderSubTab(page, 'rubric');
|
|
4555
4578
|
await clickRowMenuAction(page, currentName, GRADER_LABELS.menu.edit);
|
|
4556
4579
|
await submitCriterionModal(page, updates);
|
|
4557
|
-
await expect(criterionRow(page, updates.name)).toBeVisible({ timeout: MUTATION_TIMEOUT });
|
|
4580
|
+
await expect(criterionRow(page, updates.name)).toBeVisible({ timeout: MUTATION_TIMEOUT$1 });
|
|
4558
4581
|
logger.info(`Edited rubric criterion "${currentName}" → "${updates.name}"`);
|
|
4559
4582
|
}
|
|
4560
4583
|
/**
|
|
@@ -4566,12 +4589,12 @@ async function deleteGraderCriterion(page, name) {
|
|
|
4566
4589
|
await switchToGraderSubTab(page, 'rubric');
|
|
4567
4590
|
await clickRowMenuAction(page, name, GRADER_LABELS.menu.delete);
|
|
4568
4591
|
const modal = page.getByTestId('grader-criterion-delete-modal');
|
|
4569
|
-
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4592
|
+
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4570
4593
|
const confirmButton = modal.getByTestId('grader-criterion-delete-confirm');
|
|
4571
|
-
await expect(confirmButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
4594
|
+
await expect(confirmButton).toBeEnabled({ timeout: UI_TIMEOUT$1 });
|
|
4572
4595
|
await confirmButton.click();
|
|
4573
|
-
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
4574
|
-
await expect(criterionRow(page, name)).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
4596
|
+
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT$1 });
|
|
4597
|
+
await expect(criterionRow(page, name)).toBeHidden({ timeout: MUTATION_TIMEOUT$1 });
|
|
4575
4598
|
logger.info(`Deleted rubric criterion "${name}"`);
|
|
4576
4599
|
}
|
|
4577
4600
|
/**
|
|
@@ -4582,18 +4605,18 @@ async function deleteGraderCriterion(page, name) {
|
|
|
4582
4605
|
async function expectLastCriterionDeleteDisabled(page, name) {
|
|
4583
4606
|
await switchToGraderSubTab(page, 'rubric');
|
|
4584
4607
|
await expect(criteriaSection(page).getByTestId('grader-last-criterion-hint')).toBeVisible({
|
|
4585
|
-
timeout: UI_TIMEOUT,
|
|
4608
|
+
timeout: UI_TIMEOUT$1,
|
|
4586
4609
|
});
|
|
4587
4610
|
const trigger = criterionRow(page, name).getByRole('button', {
|
|
4588
4611
|
name: GRADER_LABELS.menu.actionsAria(name),
|
|
4589
4612
|
});
|
|
4590
|
-
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4613
|
+
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4591
4614
|
await trigger.click();
|
|
4592
4615
|
const deleteItem = page.getByRole('menuitem', { name: GRADER_LABELS.menu.delete, exact: true });
|
|
4593
|
-
await expect(deleteItem).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4616
|
+
await expect(deleteItem).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4594
4617
|
await expect(deleteItem).toHaveAttribute('aria-disabled', 'true');
|
|
4595
4618
|
await page.keyboard.press('Escape');
|
|
4596
|
-
await expect(deleteItem).toBeHidden({ timeout: UI_TIMEOUT });
|
|
4619
|
+
await expect(deleteItem).toBeHidden({ timeout: UI_TIMEOUT$1 });
|
|
4597
4620
|
}
|
|
4598
4621
|
/**
|
|
4599
4622
|
* Assert whether the amber misconfiguration banner is shown (grading on
|
|
@@ -4603,7 +4626,7 @@ async function expectLastCriterionDeleteDisabled(page, name) {
|
|
|
4603
4626
|
async function expectGraderMisconfiguredWarning(page, visible) {
|
|
4604
4627
|
const warning = graderTabBody(page).getByTestId('grader-misconfigured-warning');
|
|
4605
4628
|
if (visible) {
|
|
4606
|
-
await expect(warning).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4629
|
+
await expect(warning).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4607
4630
|
}
|
|
4608
4631
|
else {
|
|
4609
4632
|
await expect(warning).toBeHidden();
|
|
@@ -4612,7 +4635,7 @@ async function expectGraderMisconfiguredWarning(page, visible) {
|
|
|
4612
4635
|
/** Assert the rubric's live "total possible points" readout (Rubric sub-tab). */
|
|
4613
4636
|
async function expectGraderTotalPoints(page, total) {
|
|
4614
4637
|
await switchToGraderSubTab(page, 'rubric');
|
|
4615
|
-
await expect(criteriaSection(page).getByText(`Total Possible Points: ${total}`, { exact: true })).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4638
|
+
await expect(criteriaSection(page).getByText(`Total Possible Points: ${total}`, { exact: true })).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4616
4639
|
}
|
|
4617
4640
|
/** The Results sub-tab's section within the tab body. */
|
|
4618
4641
|
function resultsSection(page) {
|
|
@@ -4632,21 +4655,21 @@ function gradeResultRow(page, email) {
|
|
|
4632
4655
|
async function filterGradeResultsByEmail(page, email) {
|
|
4633
4656
|
await switchToGraderSubTab(page, 'results');
|
|
4634
4657
|
const trigger = resultsSection(page).getByTestId('grader-results-user-filter');
|
|
4635
|
-
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4658
|
+
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4636
4659
|
await trigger.click();
|
|
4637
4660
|
const searchBox = resultsSection(page).getByPlaceholder(GRADER_LABELS.results.searchUsersPlaceholder);
|
|
4638
|
-
await expect(searchBox).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4661
|
+
await expect(searchBox).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4639
4662
|
await searchBox.fill(email);
|
|
4640
4663
|
const option = resultsSection(page).getByRole('option', { name: email });
|
|
4641
|
-
await expect(option).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4664
|
+
await expect(option).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4642
4665
|
await option.click();
|
|
4643
|
-
await expect(gradeResultRow(page, email)).toBeVisible({ timeout: MUTATION_TIMEOUT });
|
|
4666
|
+
await expect(gradeResultRow(page, email)).toBeVisible({ timeout: MUTATION_TIMEOUT$1 });
|
|
4644
4667
|
logger.info(`Filtered grade results by learner "${email}"`);
|
|
4645
4668
|
}
|
|
4646
4669
|
/** Assert a grade-result row for the given learner email is visible. */
|
|
4647
4670
|
async function expectGradeResultRow(page, email) {
|
|
4648
4671
|
await switchToGraderSubTab(page, 'results');
|
|
4649
|
-
await expect(gradeResultRow(page, email)).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4672
|
+
await expect(gradeResultRow(page, email)).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4650
4673
|
}
|
|
4651
4674
|
/**
|
|
4652
4675
|
* Override a learner's grade via the row's three-dots menu → Override grade
|
|
@@ -4658,10 +4681,10 @@ async function overrideGradeResult(page, email, values) {
|
|
|
4658
4681
|
const overrideButton = gradeResultRow(page, email).getByRole('button', {
|
|
4659
4682
|
name: GRADER_LABELS.results.overrideButtonAria(email),
|
|
4660
4683
|
});
|
|
4661
|
-
await expect(overrideButton).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4684
|
+
await expect(overrideButton).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4662
4685
|
await overrideButton.click();
|
|
4663
4686
|
const modal = page.getByTestId('grader-override-modal');
|
|
4664
|
-
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4687
|
+
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4665
4688
|
await modal
|
|
4666
4689
|
.getByLabel(GRADER_LABELS.results.modal.pointsLabel, { exact: true })
|
|
4667
4690
|
.fill(String(values.points));
|
|
@@ -4671,9 +4694,9 @@ async function overrideGradeResult(page, email, values) {
|
|
|
4671
4694
|
.fill(values.feedback);
|
|
4672
4695
|
}
|
|
4673
4696
|
const saveButton = modal.getByTestId('grader-override-save');
|
|
4674
|
-
await expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
4697
|
+
await expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT$1 });
|
|
4675
4698
|
await saveButton.click();
|
|
4676
|
-
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
4699
|
+
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT$1 });
|
|
4677
4700
|
logger.info(`Overrode grade for "${email}" with ${values.points} points`);
|
|
4678
4701
|
}
|
|
4679
4702
|
/**
|
|
@@ -4685,14 +4708,14 @@ async function clearGradeResultOverride(page, email) {
|
|
|
4685
4708
|
const overrideButton = gradeResultRow(page, email).getByRole('button', {
|
|
4686
4709
|
name: GRADER_LABELS.results.overrideButtonAria(email),
|
|
4687
4710
|
});
|
|
4688
|
-
await expect(overrideButton).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4711
|
+
await expect(overrideButton).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4689
4712
|
await overrideButton.click();
|
|
4690
4713
|
const modal = page.getByTestId('grader-override-modal');
|
|
4691
|
-
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT });
|
|
4714
|
+
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT$1 });
|
|
4692
4715
|
const clearButton = modal.getByTestId('grader-override-clear');
|
|
4693
|
-
await expect(clearButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
4716
|
+
await expect(clearButton).toBeEnabled({ timeout: UI_TIMEOUT$1 });
|
|
4694
4717
|
await clearButton.click();
|
|
4695
|
-
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
4718
|
+
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT$1 });
|
|
4696
4719
|
logger.info(`Cleared grade override for "${email}"`);
|
|
4697
4720
|
}
|
|
4698
4721
|
|
|
@@ -4891,6 +4914,653 @@ async function clickBillingManageUsage(page) {
|
|
|
4891
4914
|
await btn.click();
|
|
4892
4915
|
}
|
|
4893
4916
|
|
|
4917
|
+
/**
|
|
4918
|
+
* Spend limits ("Billing") helpers — Playwright bindings for the LLM
|
|
4919
|
+
* spend-cap admin UI from `@iblai/web-containers`:
|
|
4920
|
+
*
|
|
4921
|
+
* - the **agent settings Billing tab** (`AgentSpendCapsTab`) with its
|
|
4922
|
+
* "This Agent" / "Per User" sub-tabs, the per-user table with a
|
|
4923
|
+
* three-dots row menu, and the add/edit user-limit modal (email-based
|
|
4924
|
+
* user picker), and
|
|
4925
|
+
* - the **tenant settings Billing tab** with its "Plan & Credits" /
|
|
4926
|
+
* "Spend Limits" tabs and the workspace-wide limit section
|
|
4927
|
+
* (`SpendLimitsSection` inside `BillingTab`).
|
|
4928
|
+
*
|
|
4929
|
+
* Selector policy (flakiness-proof by construction):
|
|
4930
|
+
* - Dialog-first scoping: every modal is resolved into a Locator variable
|
|
4931
|
+
* first (`user-cap-modal`, the delete dialogs, the edit-agent dialog, the
|
|
4932
|
+
* Agent Limits manage popup) and all sub-elements are queried from that
|
|
4933
|
+
* variable — never a bare page-wide match that could hit same-named
|
|
4934
|
+
* elements in nested portals.
|
|
4935
|
+
* - Nested dialogs: the tenant Agent Limits tab opens the agent Billing
|
|
4936
|
+
* editor in a popup *on top of* the tenant settings dialog, so several
|
|
4937
|
+
* dialogs with tablists can be open at once. Every agent-scope helper
|
|
4938
|
+
* accepts an optional `scope` Locator (the host dialog) for that case;
|
|
4939
|
+
* `openAgentLimitsManage` returns the popup Locator to pass as `scope`.
|
|
4940
|
+
* - Stable hooks only: `data-testid`, role + accessible name (aria-labels).
|
|
4941
|
+
* No CSS class or structural selectors.
|
|
4942
|
+
* - No `waitForTimeout` / `networkidle`. Progress is gated on UI state that
|
|
4943
|
+
* only exists after the awaited transition: a section testid rendering, a
|
|
4944
|
+
* modal closing after its mutation resolves, a table row
|
|
4945
|
+
* appearing/disappearing after the list refetch, a success toast.
|
|
4946
|
+
*/
|
|
4947
|
+
const SPEND_LIMITS_LABELS = {
|
|
4948
|
+
/** Agent-settings modal tab name (host registries label it "Billing"). */
|
|
4949
|
+
tabName: 'Billing',
|
|
4950
|
+
subTabs: {
|
|
4951
|
+
agent: 'This Agent',
|
|
4952
|
+
users: 'Per User',
|
|
4953
|
+
},
|
|
4954
|
+
tenantTabs: {
|
|
4955
|
+
planAndCredits: 'Plan & Credits',
|
|
4956
|
+
spendLimits: 'Spend Limits',
|
|
4957
|
+
agentLimits: 'Agent Limits',
|
|
4958
|
+
},
|
|
4959
|
+
/** aria-label of the tenant Billing tab's tablist. */
|
|
4960
|
+
tenantTablistAria: 'Billing sections',
|
|
4961
|
+
addUserLimitButton: 'Add User Limit',
|
|
4962
|
+
menu: {
|
|
4963
|
+
/**
|
|
4964
|
+
* aria-label template for a table row's three-dots trigger. Takes the
|
|
4965
|
+
* DISPLAYED name — the user's email when the API provides one, username
|
|
4966
|
+
* only as fallback. Helpers locate the trigger by its username-keyed
|
|
4967
|
+
* testid instead (`user-cap-actions-<username>`), which is immune to
|
|
4968
|
+
* that distinction; prefer the testid in new code.
|
|
4969
|
+
*/
|
|
4970
|
+
actionsAria: (displayName) => `Actions for ${displayName}`,
|
|
4971
|
+
edit: 'Edit',
|
|
4972
|
+
delete: 'Delete',
|
|
4973
|
+
},
|
|
4974
|
+
toasts: {
|
|
4975
|
+
saved: 'Spend limit saved',
|
|
4976
|
+
deleted: 'Spend limit deleted',
|
|
4977
|
+
},
|
|
4978
|
+
intervalOptions: {
|
|
4979
|
+
day: 'Day',
|
|
4980
|
+
week: 'Week',
|
|
4981
|
+
month: 'Month',
|
|
4982
|
+
year: 'Year',
|
|
4983
|
+
},
|
|
4984
|
+
enforcementOptions: {
|
|
4985
|
+
block: 'Block Requests',
|
|
4986
|
+
alert_only: 'Alert Only',
|
|
4987
|
+
},
|
|
4988
|
+
};
|
|
4989
|
+
const UI_TIMEOUT = 10000;
|
|
4990
|
+
const MUTATION_TIMEOUT = 15000;
|
|
4991
|
+
/**
|
|
4992
|
+
* The dialog that hosts the agent settings tabs. Several dialogs can be
|
|
4993
|
+
* open at once (the Agent Limits manage popup stacks on top of the tenant
|
|
4994
|
+
* settings dialog, and both host a tablist), so this resolves to the
|
|
4995
|
+
* TOPMOST match — stacked Radix dialogs portal to the end of `<body>` in
|
|
4996
|
+
* mount order, making the last match the one on top. Helpers that must
|
|
4997
|
+
* target a specific dialog take an explicit `scope` Locator instead, which
|
|
4998
|
+
* always wins over this default.
|
|
4999
|
+
*/
|
|
5000
|
+
function editAgentDialog(page) {
|
|
5001
|
+
return page
|
|
5002
|
+
.getByRole('dialog')
|
|
5003
|
+
.filter({ has: page.getByRole('tablist') })
|
|
5004
|
+
.last();
|
|
5005
|
+
}
|
|
5006
|
+
/**
|
|
5007
|
+
* The agent Billing tab's body. `scope` is the dialog hosting the editor
|
|
5008
|
+
* (e.g. the Locator returned by `openAgentLimitsManage`); defaults to the
|
|
5009
|
+
* topmost settings dialog.
|
|
5010
|
+
*/
|
|
5011
|
+
function spendLimitsTabBody(page, scope) {
|
|
5012
|
+
return (scope !== null && scope !== void 0 ? scope : editAgentDialog(page)).getByTestId('spend-caps-tab-body');
|
|
5013
|
+
}
|
|
5014
|
+
/** The per-user limits section within the Billing tab body. */
|
|
5015
|
+
function userCapsSection(page, scope) {
|
|
5016
|
+
return spendLimitsTabBody(page, scope).getByTestId('user-caps-section');
|
|
5017
|
+
}
|
|
5018
|
+
/** A per-user table row for the given username. */
|
|
5019
|
+
function userSpendLimitRow(page, username, scope) {
|
|
5020
|
+
return userCapsSection(page, scope).getByTestId(`user-cap-row-${username}`);
|
|
5021
|
+
}
|
|
5022
|
+
/**
|
|
5023
|
+
* Returns false if the Billing tab isn't currently rendered in the
|
|
5024
|
+
* edit-agent dialog (host didn't register it, or RBAC hid it).
|
|
5025
|
+
*/
|
|
5026
|
+
async function isSpendLimitsTabVisible(page) {
|
|
5027
|
+
const tab = editAgentDialog(page).getByRole('tab', {
|
|
5028
|
+
name: SPEND_LIMITS_LABELS.tabName,
|
|
5029
|
+
exact: true,
|
|
5030
|
+
});
|
|
5031
|
+
try {
|
|
5032
|
+
await expect(tab).toBeVisible({ timeout: 5000 });
|
|
5033
|
+
return true;
|
|
5034
|
+
}
|
|
5035
|
+
catch (_a) {
|
|
5036
|
+
return false;
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
/**
|
|
5040
|
+
* Switch to the Billing top-level tab of the edit-agent dialog. Completion
|
|
5041
|
+
* is gated on the tab body's testid, not on timing.
|
|
5042
|
+
*/
|
|
5043
|
+
async function switchToSpendLimitsTab(page) {
|
|
5044
|
+
const dialog = editAgentDialog(page);
|
|
5045
|
+
const tab = dialog.getByRole('tab', { name: SPEND_LIMITS_LABELS.tabName, exact: true });
|
|
5046
|
+
await expect(tab).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5047
|
+
await tab.click();
|
|
5048
|
+
await expect(spendLimitsTabBody(page)).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5049
|
+
logger.info('Switched to agent Billing (spend limits) tab');
|
|
5050
|
+
}
|
|
5051
|
+
/**
|
|
5052
|
+
* Switch between the Billing tab's two sub-tabs. Completion is gated on the
|
|
5053
|
+
* sub-tab's content rendering: the agent limit form (or its loading/denied
|
|
5054
|
+
* states) for "agent", the per-user section (or its spinner) for "users".
|
|
5055
|
+
* `scope` is the dialog hosting the editor (see `spendLimitsTabBody`).
|
|
5056
|
+
*/
|
|
5057
|
+
async function switchToSpendLimitsSubTab(page, subTab, scope) {
|
|
5058
|
+
const body = spendLimitsTabBody(page, scope);
|
|
5059
|
+
const trigger = body.getByTestId(`spend-caps-sub-tab-${subTab}`);
|
|
5060
|
+
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5061
|
+
await trigger.click();
|
|
5062
|
+
const content = subTab === 'agent'
|
|
5063
|
+
? body
|
|
5064
|
+
.getByTestId('agent-cap-form')
|
|
5065
|
+
.or(body.getByTestId('agent-cap-loading'))
|
|
5066
|
+
.or(body.getByTestId('spend-caps-denied'))
|
|
5067
|
+
: body
|
|
5068
|
+
.getByTestId('user-caps-section')
|
|
5069
|
+
.or(body.getByTestId('user-caps-loading'))
|
|
5070
|
+
.or(body.getByTestId('spend-caps-denied'));
|
|
5071
|
+
await expect(content.first()).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5072
|
+
logger.info(`Switched to spend limits "${subTab}" sub-tab`);
|
|
5073
|
+
}
|
|
5074
|
+
/** Pick an option in one of the limit form's Radix selects. */
|
|
5075
|
+
async function pickSelectOption(scope, page, triggerTestId, label) {
|
|
5076
|
+
const trigger = scope.getByTestId(triggerTestId);
|
|
5077
|
+
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5078
|
+
await trigger.click();
|
|
5079
|
+
// Radix renders the listbox in a portal outside the scope — the open
|
|
5080
|
+
// listbox is the only one in the document, so a role-level lookup is safe.
|
|
5081
|
+
const option = page.getByRole('option', { name: label, exact: true });
|
|
5082
|
+
await expect(option).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5083
|
+
await option.click();
|
|
5084
|
+
await expect(option).toBeHidden({ timeout: UI_TIMEOUT });
|
|
5085
|
+
}
|
|
5086
|
+
/**
|
|
5087
|
+
* Fill a spend-limit form (any scope — agent, workspace, or the user-limit
|
|
5088
|
+
* modal). Only the provided fields are changed. `testIdPrefix` matches the
|
|
5089
|
+
* form's testId prop: 'agent-cap', 'tenant-cap' or 'user-cap-modal'.
|
|
5090
|
+
*/
|
|
5091
|
+
async function fillSpendLimitForm(page, scope, testIdPrefix, values) {
|
|
5092
|
+
if (values.enabled !== undefined) {
|
|
5093
|
+
const toggle = scope.getByTestId(`${testIdPrefix}-enabled-switch`);
|
|
5094
|
+
await expect(toggle).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5095
|
+
if ((await toggle.getAttribute('aria-checked')) !== String(values.enabled)) {
|
|
5096
|
+
await toggle.click();
|
|
5097
|
+
await expect(toggle).toHaveAttribute('aria-checked', String(values.enabled), {
|
|
5098
|
+
timeout: UI_TIMEOUT,
|
|
5099
|
+
});
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
if (values.limitUsd !== undefined) {
|
|
5103
|
+
const input = scope.getByTestId(`${testIdPrefix}-limit-input`);
|
|
5104
|
+
await expect(input).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5105
|
+
await input.fill(String(values.limitUsd));
|
|
5106
|
+
}
|
|
5107
|
+
if (values.interval) {
|
|
5108
|
+
await pickSelectOption(scope, page, `${testIdPrefix}-interval-select`, SPEND_LIMITS_LABELS.intervalOptions[values.interval]);
|
|
5109
|
+
}
|
|
5110
|
+
if (values.enforcement) {
|
|
5111
|
+
await pickSelectOption(scope, page, `${testIdPrefix}-enforcement-select`, SPEND_LIMITS_LABELS.enforcementOptions[values.enforcement]);
|
|
5112
|
+
}
|
|
5113
|
+
if (values.alertThresholds !== undefined) {
|
|
5114
|
+
const input = scope.getByTestId(`${testIdPrefix}-thresholds-input`);
|
|
5115
|
+
await expect(input).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5116
|
+
await input.fill(values.alertThresholds);
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
/** Click a form's Save and gate on the saved toast (fires after the PUT resolves). */
|
|
5120
|
+
async function saveSpendLimitForm(page, scope, testIdPrefix) {
|
|
5121
|
+
const saveButton = scope.getByTestId(`${testIdPrefix}-save-button`);
|
|
5122
|
+
await expect(saveButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
5123
|
+
await saveButton.click();
|
|
5124
|
+
// .first() is deliberate: repeated saves can stack identical sonner
|
|
5125
|
+
// toasts, and any one of them proves the mutation resolved.
|
|
5126
|
+
await expect(page.getByText(SPEND_LIMITS_LABELS.toasts.saved).first()).toBeVisible({
|
|
5127
|
+
timeout: MUTATION_TIMEOUT,
|
|
5128
|
+
});
|
|
5129
|
+
}
|
|
5130
|
+
/** Confirm a captured delete dialog and gate on it closing + the deleted toast. */
|
|
5131
|
+
async function confirmDeleteDialog(page, dialogTestId) {
|
|
5132
|
+
const dialog = page.getByTestId(dialogTestId);
|
|
5133
|
+
await expect(dialog).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5134
|
+
// All three delete dialogs share the '<x>-delete-modal' / '<x>-delete-confirm'
|
|
5135
|
+
// testid convention.
|
|
5136
|
+
const confirmButton = dialog.getByTestId(`${dialogTestId.replace('-modal', '')}-confirm`);
|
|
5137
|
+
await expect(confirmButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
5138
|
+
await confirmButton.click();
|
|
5139
|
+
await expect(dialog).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
5140
|
+
await expect(page.getByText(SPEND_LIMITS_LABELS.toasts.deleted).first()).toBeVisible({
|
|
5141
|
+
timeout: MUTATION_TIMEOUT,
|
|
5142
|
+
});
|
|
5143
|
+
}
|
|
5144
|
+
/**
|
|
5145
|
+
* Create or update the agent-scoped spend limit (switches to the "This
|
|
5146
|
+
* agent" sub-tab first). Completion is gated on the saved toast and the
|
|
5147
|
+
* usage strip rendering (it only exists once a cap is persisted).
|
|
5148
|
+
*/
|
|
5149
|
+
async function setAgentSpendLimit(page, values, scope) {
|
|
5150
|
+
await switchToSpendLimitsSubTab(page, 'agent', scope);
|
|
5151
|
+
const body = spendLimitsTabBody(page, scope);
|
|
5152
|
+
await fillSpendLimitForm(page, body, 'agent-cap', values);
|
|
5153
|
+
await saveSpendLimitForm(page, body, 'agent-cap');
|
|
5154
|
+
await expect(body.getByTestId('agent-cap-usage')).toBeVisible({ timeout: MUTATION_TIMEOUT });
|
|
5155
|
+
logger.info('Saved agent spend limit');
|
|
5156
|
+
}
|
|
5157
|
+
/**
|
|
5158
|
+
* Delete the agent-scoped spend limit via the form's Delete button →
|
|
5159
|
+
* confirmation dialog. Completion is gated on the dialog closing, the
|
|
5160
|
+
* deleted toast, and the form returning to create mode (no-cap hint).
|
|
5161
|
+
*/
|
|
5162
|
+
async function deleteAgentSpendLimit(page, scope) {
|
|
5163
|
+
await switchToSpendLimitsSubTab(page, 'agent', scope);
|
|
5164
|
+
const body = spendLimitsTabBody(page, scope);
|
|
5165
|
+
const deleteButton = body.getByTestId('agent-cap-delete-button');
|
|
5166
|
+
await expect(deleteButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
5167
|
+
await deleteButton.click();
|
|
5168
|
+
await confirmDeleteDialog(page, 'spend-cap-delete-modal');
|
|
5169
|
+
await expect(body.getByTestId('agent-cap-no-cap-hint')).toBeVisible({
|
|
5170
|
+
timeout: MUTATION_TIMEOUT,
|
|
5171
|
+
});
|
|
5172
|
+
logger.info('Deleted agent spend limit');
|
|
5173
|
+
}
|
|
5174
|
+
/**
|
|
5175
|
+
* Fill the user-limit modal's form and save it. The modal Locator is
|
|
5176
|
+
* captured once and every sub-element is resolved from it.
|
|
5177
|
+
*/
|
|
5178
|
+
async function submitUserCapModal(page, values) {
|
|
5179
|
+
const modal = page.getByTestId('user-cap-modal');
|
|
5180
|
+
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5181
|
+
await fillSpendLimitForm(page, modal, 'user-cap-modal', values);
|
|
5182
|
+
await saveSpendLimitForm(page, modal, 'user-cap-modal');
|
|
5183
|
+
// The modal only closes itself after the upsert resolves.
|
|
5184
|
+
await expect(modal).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
5185
|
+
}
|
|
5186
|
+
/**
|
|
5187
|
+
* Add a per-user spend limit through the modal (switches to the "Per User"
|
|
5188
|
+
* sub-tab first): opens the modal, searches the user by email, clicks the
|
|
5189
|
+
* email in the results (the picker is email-only by design), fills the
|
|
5190
|
+
* form, saves. Completion is gated on the modal closing and the user's
|
|
5191
|
+
* table row rendering after the list refetch.
|
|
5192
|
+
*/
|
|
5193
|
+
async function addUserSpendLimit(page, values, scope) {
|
|
5194
|
+
await switchToSpendLimitsSubTab(page, 'users', scope);
|
|
5195
|
+
const addButton = userCapsSection(page, scope).getByTestId('user-caps-add-button');
|
|
5196
|
+
await expect(addButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
5197
|
+
await addButton.click();
|
|
5198
|
+
const modal = page.getByTestId('user-cap-modal');
|
|
5199
|
+
await expect(modal).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5200
|
+
const pickerInput = modal.getByTestId('user-cap-modal-user-picker-input');
|
|
5201
|
+
await expect(pickerInput).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5202
|
+
await pickerInput.fill(values.email);
|
|
5203
|
+
// Options render as buttons labelled with the email only.
|
|
5204
|
+
const option = modal
|
|
5205
|
+
.getByTestId('user-cap-modal-user-picker-results')
|
|
5206
|
+
.getByRole('button', { name: values.email, exact: true });
|
|
5207
|
+
await expect(option).toBeVisible({ timeout: MUTATION_TIMEOUT });
|
|
5208
|
+
await option.click();
|
|
5209
|
+
await expect(modal.getByTestId('user-cap-modal-user-picker-selected')).toBeVisible({
|
|
5210
|
+
timeout: UI_TIMEOUT,
|
|
5211
|
+
});
|
|
5212
|
+
await submitUserCapModal(page, values);
|
|
5213
|
+
await expect(userSpendLimitRow(page, values.username, scope)).toBeVisible({
|
|
5214
|
+
timeout: MUTATION_TIMEOUT,
|
|
5215
|
+
});
|
|
5216
|
+
logger.info(`Added user spend limit for "${values.email}"`);
|
|
5217
|
+
}
|
|
5218
|
+
/**
|
|
5219
|
+
* Open a per-user table row's three-dots menu and click one of its actions.
|
|
5220
|
+
* The trigger is located by testid, not aria-label — the accessible name is
|
|
5221
|
+
* email-first while rows stay keyed by username.
|
|
5222
|
+
*/
|
|
5223
|
+
async function clickUserRowMenuAction(page, username, action, scope) {
|
|
5224
|
+
const trigger = userSpendLimitRow(page, username, scope).getByTestId(`user-cap-actions-${username}`);
|
|
5225
|
+
await expect(trigger).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5226
|
+
await trigger.click();
|
|
5227
|
+
// The menu portals to the page root — the open menu is the only one.
|
|
5228
|
+
const item = page.getByRole('menuitem', { name: action, exact: true });
|
|
5229
|
+
await expect(item).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5230
|
+
await item.click();
|
|
5231
|
+
}
|
|
5232
|
+
/**
|
|
5233
|
+
* Edit an existing per-user limit via its row's three-dots menu → Edit
|
|
5234
|
+
* modal. Completion is gated on the modal closing after the upsert.
|
|
5235
|
+
*/
|
|
5236
|
+
async function editUserSpendLimit(page, username, values, scope) {
|
|
5237
|
+
await switchToSpendLimitsSubTab(page, 'users', scope);
|
|
5238
|
+
await clickUserRowMenuAction(page, username, SPEND_LIMITS_LABELS.menu.edit, scope);
|
|
5239
|
+
await submitUserCapModal(page, values);
|
|
5240
|
+
logger.info(`Edited user spend limit for "${username}"`);
|
|
5241
|
+
}
|
|
5242
|
+
/**
|
|
5243
|
+
* Idempotently flip a per-user limit's Status toggle (the switch in the
|
|
5244
|
+
* table's Status column — datasets-table pattern, saves immediately).
|
|
5245
|
+
* Completion is gated on the saved toast and the switch reflecting the new
|
|
5246
|
+
* state after the row refetch.
|
|
5247
|
+
*/
|
|
5248
|
+
async function setUserSpendLimitEnabled(page, username, enabled, scope) {
|
|
5249
|
+
await switchToSpendLimitsSubTab(page, 'users', scope);
|
|
5250
|
+
const toggle = userSpendLimitRow(page, username, scope).getByTestId(`user-cap-status-switch-${username}`);
|
|
5251
|
+
await expect(toggle).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5252
|
+
if ((await toggle.getAttribute('aria-checked')) === String(enabled)) {
|
|
5253
|
+
logger.info(`User spend limit for "${username}" already ${enabled ? 'enabled' : 'disabled'}`);
|
|
5254
|
+
return;
|
|
5255
|
+
}
|
|
5256
|
+
await expect(toggle).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
5257
|
+
await toggle.click();
|
|
5258
|
+
await expect(page.getByText(SPEND_LIMITS_LABELS.toasts.saved).first()).toBeVisible({
|
|
5259
|
+
timeout: MUTATION_TIMEOUT,
|
|
5260
|
+
});
|
|
5261
|
+
await expect(toggle).toHaveAttribute('aria-checked', String(enabled), {
|
|
5262
|
+
timeout: MUTATION_TIMEOUT,
|
|
5263
|
+
});
|
|
5264
|
+
logger.info(`User spend limit for "${username}" ${enabled ? 'enabled' : 'disabled'}`);
|
|
5265
|
+
}
|
|
5266
|
+
/**
|
|
5267
|
+
* Delete a per-user limit via its row's three-dots menu → confirmation
|
|
5268
|
+
* dialog. Completion is gated on the dialog closing, the deleted toast and
|
|
5269
|
+
* the row disappearing after the list refetch.
|
|
5270
|
+
*/
|
|
5271
|
+
async function deleteUserSpendLimit(page, username, scope) {
|
|
5272
|
+
await switchToSpendLimitsSubTab(page, 'users', scope);
|
|
5273
|
+
await clickUserRowMenuAction(page, username, SPEND_LIMITS_LABELS.menu.delete, scope);
|
|
5274
|
+
await confirmDeleteDialog(page, 'user-cap-delete-modal');
|
|
5275
|
+
await expect(userSpendLimitRow(page, username, scope)).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
5276
|
+
logger.info(`Deleted user spend limit for "${username}"`);
|
|
5277
|
+
}
|
|
5278
|
+
// ---------------------------------------------------------------------------
|
|
5279
|
+
// Tenant settings — Billing tab ("Plan & Credits" / "Spend Limits")
|
|
5280
|
+
// ---------------------------------------------------------------------------
|
|
5281
|
+
/** The tenant Billing tab's tablist (scoped by its aria-label). */
|
|
5282
|
+
function tenantBillingTablist(page) {
|
|
5283
|
+
return page.getByRole('tablist', { name: SPEND_LIMITS_LABELS.tenantTablistAria });
|
|
5284
|
+
}
|
|
5285
|
+
/** The workspace spend limit section card in the tenant Billing tab. */
|
|
5286
|
+
function workspaceSpendLimitSection(page) {
|
|
5287
|
+
return page.getByTestId('spend-limits-workspace-section');
|
|
5288
|
+
}
|
|
5289
|
+
/**
|
|
5290
|
+
* Switch the tenant Billing tab to "Spend Limits". Assumes the tenant
|
|
5291
|
+
* settings Billing tab is already open (see `waitForBillingTabReady`).
|
|
5292
|
+
* Completion is gated on the workspace section rendering.
|
|
5293
|
+
*/
|
|
5294
|
+
async function switchToWorkspaceSpendLimits(page) {
|
|
5295
|
+
const tab = tenantBillingTablist(page).getByRole('tab', {
|
|
5296
|
+
name: SPEND_LIMITS_LABELS.tenantTabs.spendLimits,
|
|
5297
|
+
exact: true,
|
|
5298
|
+
});
|
|
5299
|
+
await expect(tab).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5300
|
+
await tab.click();
|
|
5301
|
+
await expect(workspaceSpendLimitSection(page)).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5302
|
+
logger.info('Switched to tenant Spend Limits tab');
|
|
5303
|
+
}
|
|
5304
|
+
/**
|
|
5305
|
+
* Switch the tenant Billing tab back to "Plan & Credits". Completion is
|
|
5306
|
+
* gated on the Plan section rendering.
|
|
5307
|
+
*/
|
|
5308
|
+
async function switchToPlanAndCredits(page) {
|
|
5309
|
+
const tab = tenantBillingTablist(page).getByRole('tab', {
|
|
5310
|
+
name: SPEND_LIMITS_LABELS.tenantTabs.planAndCredits,
|
|
5311
|
+
exact: true,
|
|
5312
|
+
});
|
|
5313
|
+
await expect(tab).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5314
|
+
await tab.click();
|
|
5315
|
+
await expect(page.getByTestId('billing-plan-section')).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5316
|
+
logger.info('Switched to tenant Plan & Credits tab');
|
|
5317
|
+
}
|
|
5318
|
+
/**
|
|
5319
|
+
* Create or update the workspace-wide spend limit (switches to the Spend
|
|
5320
|
+
* Limits tab first). Completion is gated on the saved toast and the
|
|
5321
|
+
* usage strip rendering (it only exists once a cap is persisted).
|
|
5322
|
+
*/
|
|
5323
|
+
async function setWorkspaceSpendLimit(page, values) {
|
|
5324
|
+
await switchToWorkspaceSpendLimits(page);
|
|
5325
|
+
const section = workspaceSpendLimitSection(page);
|
|
5326
|
+
await fillSpendLimitForm(page, section, 'tenant-cap', values);
|
|
5327
|
+
await saveSpendLimitForm(page, section, 'tenant-cap');
|
|
5328
|
+
await expect(section.getByTestId('tenant-cap-usage')).toBeVisible({
|
|
5329
|
+
timeout: MUTATION_TIMEOUT,
|
|
5330
|
+
});
|
|
5331
|
+
logger.info('Saved workspace spend limit');
|
|
5332
|
+
}
|
|
5333
|
+
/**
|
|
5334
|
+
* Delete the workspace-wide spend limit. Completion is gated on the dialog
|
|
5335
|
+
* closing, the deleted toast, and the usage strip disappearing.
|
|
5336
|
+
*/
|
|
5337
|
+
async function deleteWorkspaceSpendLimit(page) {
|
|
5338
|
+
await switchToWorkspaceSpendLimits(page);
|
|
5339
|
+
const section = workspaceSpendLimitSection(page);
|
|
5340
|
+
const deleteButton = section.getByTestId('tenant-cap-delete-button');
|
|
5341
|
+
await expect(deleteButton).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
5342
|
+
await deleteButton.click();
|
|
5343
|
+
await confirmDeleteDialog(page, 'tenant-cap-delete-modal');
|
|
5344
|
+
await expect(section.getByTestId('tenant-cap-usage')).toBeHidden({
|
|
5345
|
+
timeout: MUTATION_TIMEOUT,
|
|
5346
|
+
});
|
|
5347
|
+
logger.info('Deleted workspace spend limit');
|
|
5348
|
+
}
|
|
5349
|
+
/**
|
|
5350
|
+
* Assert the workspace usage strip (the same one the agent editor shows) is
|
|
5351
|
+
* visible, optionally matching formatted dollar values like '$30.50' inside
|
|
5352
|
+
* its "Spent: …" / "Remaining: …" texts.
|
|
5353
|
+
*/
|
|
5354
|
+
async function expectWorkspaceSpendStats(page, values) {
|
|
5355
|
+
const section = workspaceSpendLimitSection(page);
|
|
5356
|
+
const usage = section.getByTestId('tenant-cap-usage');
|
|
5357
|
+
await expect(usage).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5358
|
+
if (values === null || values === void 0 ? void 0 : values.spent) {
|
|
5359
|
+
await expect(section.getByTestId('tenant-cap-spent')).toContainText(values.spent);
|
|
5360
|
+
}
|
|
5361
|
+
if (values === null || values === void 0 ? void 0 : values.remaining) {
|
|
5362
|
+
await expect(section.getByTestId('tenant-cap-remaining')).toContainText(values.remaining);
|
|
5363
|
+
}
|
|
5364
|
+
}
|
|
5365
|
+
/**
|
|
5366
|
+
* Assert the "actual spend" stats (financial analytics) shown while no
|
|
5367
|
+
* workspace cap is configured yet.
|
|
5368
|
+
*/
|
|
5369
|
+
async function expectWorkspaceActualSpendStats(page) {
|
|
5370
|
+
await expect(workspaceSpendLimitSection(page).getByTestId('workspace-actual-spend-stats')).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5371
|
+
}
|
|
5372
|
+
// ---------------------------------------------------------------------------
|
|
5373
|
+
// Tenant settings — Billing tab, "Agent Limits" tab (configured caps table)
|
|
5374
|
+
// ---------------------------------------------------------------------------
|
|
5375
|
+
/** The Agent Limits section card in the tenant Billing tab. */
|
|
5376
|
+
function agentLimitsSection(page) {
|
|
5377
|
+
return page.getByTestId('spend-limits-agents-section');
|
|
5378
|
+
}
|
|
5379
|
+
/** An Agent Limits table row for the given agent unique_id. */
|
|
5380
|
+
function agentLimitsRow(page, mentorUniqueId) {
|
|
5381
|
+
return agentLimitsSection(page).getByTestId(`agent-limits-row-${mentorUniqueId}`);
|
|
5382
|
+
}
|
|
5383
|
+
/**
|
|
5384
|
+
* Switch the tenant Billing tab to "Agent Limits". Assumes the tenant
|
|
5385
|
+
* Billing tab is already open. Completion is gated on the section rendering.
|
|
5386
|
+
*/
|
|
5387
|
+
async function switchToAgentLimits(page) {
|
|
5388
|
+
const tab = tenantBillingTablist(page).getByRole('tab', {
|
|
5389
|
+
name: SPEND_LIMITS_LABELS.tenantTabs.agentLimits,
|
|
5390
|
+
exact: true,
|
|
5391
|
+
});
|
|
5392
|
+
await expect(tab).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5393
|
+
await tab.click();
|
|
5394
|
+
await expect(agentLimitsSection(page)).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5395
|
+
logger.info('Switched to tenant Agent Limits tab');
|
|
5396
|
+
}
|
|
5397
|
+
/**
|
|
5398
|
+
* Filter the Agent Limits table to one agent via the autocomplete: types the
|
|
5399
|
+
* name, clicks the matching option, and gates on the selected chip.
|
|
5400
|
+
*/
|
|
5401
|
+
async function filterAgentLimits(page, agentName) {
|
|
5402
|
+
const section = agentLimitsSection(page);
|
|
5403
|
+
const input = section.getByTestId('agent-limits-filter-input');
|
|
5404
|
+
await expect(input).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5405
|
+
await input.fill(agentName);
|
|
5406
|
+
const option = section
|
|
5407
|
+
.getByTestId('agent-limits-filter-results')
|
|
5408
|
+
.getByRole('button', { name: agentName, exact: true });
|
|
5409
|
+
await expect(option).toBeVisible({ timeout: MUTATION_TIMEOUT });
|
|
5410
|
+
await option.click();
|
|
5411
|
+
await expect(section.getByTestId('agent-limits-filter-selected')).toBeVisible({
|
|
5412
|
+
timeout: UI_TIMEOUT,
|
|
5413
|
+
});
|
|
5414
|
+
logger.info(`Filtered agent limits to "${agentName}"`);
|
|
5415
|
+
}
|
|
5416
|
+
/** Clear the Agent Limits autocomplete filter (back to the full caps list). */
|
|
5417
|
+
async function clearAgentLimitsFilter(page) {
|
|
5418
|
+
const section = agentLimitsSection(page);
|
|
5419
|
+
const clearButton = section.getByTestId('agent-limits-filter-clear');
|
|
5420
|
+
await expect(clearButton).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5421
|
+
await clearButton.click();
|
|
5422
|
+
await expect(section.getByTestId('agent-limits-filter-input')).toBeVisible({
|
|
5423
|
+
timeout: UI_TIMEOUT,
|
|
5424
|
+
});
|
|
5425
|
+
}
|
|
5426
|
+
/** Gate on the Agent Limits manage popup being open with the Billing editor loaded. */
|
|
5427
|
+
async function expectAgentLimitsPopupOpen(page) {
|
|
5428
|
+
const popup = page.getByTestId('agent-limits-modal');
|
|
5429
|
+
await expect(popup).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5430
|
+
await expect(popup.getByTestId('spend-caps-tab-body')).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5431
|
+
return popup;
|
|
5432
|
+
}
|
|
5433
|
+
/**
|
|
5434
|
+
* Open the manage popup for a capped agent's row and return the popup's
|
|
5435
|
+
* Locator. The popup hosts the same `AgentSpendCapsTab` editor as the
|
|
5436
|
+
* agent's own settings and stacks ON TOP of the tenant settings dialog, so
|
|
5437
|
+
* pass the returned Locator as the `scope` argument of the agent-scope
|
|
5438
|
+
* helpers (`setAgentSpendLimit`, `addUserSpendLimit`, …) — that pins every
|
|
5439
|
+
* query to the popup instead of the dialog underneath.
|
|
5440
|
+
*/
|
|
5441
|
+
async function openAgentLimitsManage(page, mentorUniqueId) {
|
|
5442
|
+
const manageButton = agentLimitsRow(page, mentorUniqueId).getByTestId(`agent-limits-manage-${mentorUniqueId}`);
|
|
5443
|
+
await expect(manageButton).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5444
|
+
await manageButton.click();
|
|
5445
|
+
const popup = await expectAgentLimitsPopupOpen(page);
|
|
5446
|
+
logger.info(`Opened agent limits popup for "${mentorUniqueId}"`);
|
|
5447
|
+
return popup;
|
|
5448
|
+
}
|
|
5449
|
+
/**
|
|
5450
|
+
* With the filter set to an agent that has no cap yet, click the empty
|
|
5451
|
+
* state's "Set Spend Limit" button and return the opened popup's Locator
|
|
5452
|
+
* (pass it as `scope` to the agent-scope helpers, see
|
|
5453
|
+
* `openAgentLimitsManage`).
|
|
5454
|
+
*/
|
|
5455
|
+
async function clickSetSpendLimitForFilteredAgent(page) {
|
|
5456
|
+
const button = agentLimitsSection(page).getByTestId('agent-limits-set-limit-button');
|
|
5457
|
+
await expect(button).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5458
|
+
await button.click();
|
|
5459
|
+
const popup = await expectAgentLimitsPopupOpen(page);
|
|
5460
|
+
logger.info('Opened agent limits popup from the filtered empty state');
|
|
5461
|
+
return popup;
|
|
5462
|
+
}
|
|
5463
|
+
/** Close the Agent Limits manage popup (Escape) and gate on it disappearing. */
|
|
5464
|
+
async function closeAgentLimitsPopup(page) {
|
|
5465
|
+
const popup = page.getByTestId('agent-limits-modal');
|
|
5466
|
+
await expect(popup).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5467
|
+
await page.keyboard.press('Escape');
|
|
5468
|
+
await expect(popup).toBeHidden({ timeout: UI_TIMEOUT });
|
|
5469
|
+
}
|
|
5470
|
+
/**
|
|
5471
|
+
* Idempotently flip an agent cap's Status toggle in the Agent Limits table
|
|
5472
|
+
* (saves immediately). Gates on the saved toast and the switch reflecting
|
|
5473
|
+
* the new state after the list refetch.
|
|
5474
|
+
*/
|
|
5475
|
+
async function setAgentLimitsRowEnabled(page, mentorUniqueId, enabled) {
|
|
5476
|
+
const toggle = agentLimitsRow(page, mentorUniqueId).getByTestId(`agent-limits-status-switch-${mentorUniqueId}`);
|
|
5477
|
+
await expect(toggle).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5478
|
+
if ((await toggle.getAttribute('aria-checked')) === String(enabled)) {
|
|
5479
|
+
logger.info(`Agent cap "${mentorUniqueId}" already ${enabled ? 'enabled' : 'disabled'}`);
|
|
5480
|
+
return;
|
|
5481
|
+
}
|
|
5482
|
+
await expect(toggle).toBeEnabled({ timeout: UI_TIMEOUT });
|
|
5483
|
+
await toggle.click();
|
|
5484
|
+
await expect(page.getByText(SPEND_LIMITS_LABELS.toasts.saved).first()).toBeVisible({
|
|
5485
|
+
timeout: MUTATION_TIMEOUT,
|
|
5486
|
+
});
|
|
5487
|
+
await expect(toggle).toHaveAttribute('aria-checked', String(enabled), {
|
|
5488
|
+
timeout: MUTATION_TIMEOUT,
|
|
5489
|
+
});
|
|
5490
|
+
logger.info(`Agent cap "${mentorUniqueId}" ${enabled ? 'enabled' : 'disabled'}`);
|
|
5491
|
+
}
|
|
5492
|
+
/**
|
|
5493
|
+
* Assert an Agent Limits row is visible, optionally checking its formatted
|
|
5494
|
+
* cell contents, e.g. `{ name: 'Math Tutor', limit: '$250.00', spent: '$30.50' }`.
|
|
5495
|
+
* Text matching is substring-based so a limit can be checked with or without
|
|
5496
|
+
* its "/ Month" interval suffix.
|
|
5497
|
+
*/
|
|
5498
|
+
async function expectAgentLimitsRowContent(page, mentorUniqueId, texts) {
|
|
5499
|
+
const row = agentLimitsRow(page, mentorUniqueId);
|
|
5500
|
+
await expect(row).toBeVisible({ timeout: UI_TIMEOUT });
|
|
5501
|
+
for (const text of Object.values(texts !== null && texts !== void 0 ? texts : {})) {
|
|
5502
|
+
if (text)
|
|
5503
|
+
await expect(row).toContainText(text);
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
// ---------------------------------------------------------------------------
|
|
5507
|
+
// Tenant settings — end-to-end composites through the manage popup
|
|
5508
|
+
// ---------------------------------------------------------------------------
|
|
5509
|
+
/**
|
|
5510
|
+
* End to end from the tenant Agent Limits tab, for an agent with NO cap yet:
|
|
5511
|
+
* filter to the agent by name, open the popup from the empty state's "Set
|
|
5512
|
+
* Spend Limit" button, save the agent-scoped limit inside the popup, close
|
|
5513
|
+
* it, and gate on the agent's row rendering after the list refetch.
|
|
5514
|
+
*/
|
|
5515
|
+
async function createAgentSpendLimitViaFilter(page, agent, values) {
|
|
5516
|
+
await filterAgentLimits(page, agent.name);
|
|
5517
|
+
const popup = await clickSetSpendLimitForFilteredAgent(page);
|
|
5518
|
+
await setAgentSpendLimit(page, values, popup);
|
|
5519
|
+
await closeAgentLimitsPopup(page);
|
|
5520
|
+
await expect(agentLimitsRow(page, agent.mentorUniqueId)).toBeVisible({
|
|
5521
|
+
timeout: MUTATION_TIMEOUT,
|
|
5522
|
+
});
|
|
5523
|
+
logger.info(`Created agent spend limit for "${agent.name}" via the Agent Limits filter`);
|
|
5524
|
+
}
|
|
5525
|
+
/**
|
|
5526
|
+
* End to end from the tenant Agent Limits tab: open a capped agent's manage
|
|
5527
|
+
* popup, save the agent-scoped limit inside it, close the popup, and gate on
|
|
5528
|
+
* the row still rendering after the list refetch.
|
|
5529
|
+
*/
|
|
5530
|
+
async function setAgentSpendLimitFromTenantBilling(page, mentorUniqueId, values) {
|
|
5531
|
+
const popup = await openAgentLimitsManage(page, mentorUniqueId);
|
|
5532
|
+
await setAgentSpendLimit(page, values, popup);
|
|
5533
|
+
await closeAgentLimitsPopup(page);
|
|
5534
|
+
await expect(agentLimitsRow(page, mentorUniqueId)).toBeVisible({ timeout: MUTATION_TIMEOUT });
|
|
5535
|
+
logger.info(`Saved agent spend limit for "${mentorUniqueId}" from tenant Billing`);
|
|
5536
|
+
}
|
|
5537
|
+
/**
|
|
5538
|
+
* End to end from the tenant Agent Limits tab: open the agent's manage popup
|
|
5539
|
+
* and delete its agent-scoped limit through the confirmation dialog. Closing
|
|
5540
|
+
* the popup afterwards gates on the agent's row disappearing from the table
|
|
5541
|
+
* (the tenant-wide list only holds configured caps).
|
|
5542
|
+
*/
|
|
5543
|
+
async function deleteAgentSpendLimitFromTenantBilling(page, mentorUniqueId) {
|
|
5544
|
+
const popup = await openAgentLimitsManage(page, mentorUniqueId);
|
|
5545
|
+
await deleteAgentSpendLimit(page, popup);
|
|
5546
|
+
await closeAgentLimitsPopup(page);
|
|
5547
|
+
await expect(agentLimitsRow(page, mentorUniqueId)).toBeHidden({ timeout: MUTATION_TIMEOUT });
|
|
5548
|
+
logger.info(`Deleted agent spend limit for "${mentorUniqueId}" from tenant Billing`);
|
|
5549
|
+
}
|
|
5550
|
+
/**
|
|
5551
|
+
* End to end per-user limit from the tenant Agent Limits tab: open the
|
|
5552
|
+
* agent's manage popup, add the user limit through its "Per User" sub-tab
|
|
5553
|
+
* (nested user-cap modal inside the popup), then close the popup. Completion
|
|
5554
|
+
* is gated inside `addUserSpendLimit` on the modal closing and the user's
|
|
5555
|
+
* row rendering in the popup's table.
|
|
5556
|
+
*/
|
|
5557
|
+
async function addUserSpendLimitFromTenantBilling(page, mentorUniqueId, values) {
|
|
5558
|
+
const popup = await openAgentLimitsManage(page, mentorUniqueId);
|
|
5559
|
+
await addUserSpendLimit(page, values, popup);
|
|
5560
|
+
await closeAgentLimitsPopup(page);
|
|
5561
|
+
logger.info(`Added user spend limit for "${values.email}" on "${mentorUniqueId}" from tenant Billing`);
|
|
5562
|
+
}
|
|
5563
|
+
|
|
4894
5564
|
/**
|
|
4895
5565
|
* Tasks tab helpers — Playwright bindings for the `AgentTasksTab`
|
|
4896
5566
|
* component from `@iblai/web-containers`.
|
|
@@ -7178,5 +7848,5 @@ function createPlaywrightConfig(options) {
|
|
|
7178
7848
|
});
|
|
7179
7849
|
}
|
|
7180
7850
|
|
|
7181
|
-
export { AuthFlowBuilder, CHAT_PRIVACY_LABELS, CustomReporter, EVALS_LABELS, GRADER_LABELS, LTI_LABELS, LTI_TEST_IDS, MailsacClient, PRIVACY_LABELS, SCREENSHARE_LABELS, SUPPORT_LABELS, TASKS_LABELS, VOICE_LABELS, addGraderCriterion, addManualScore, addMemory, addQaPairsManually, addSkillToAgent, addTextResource, archiveFirstMemory, archiveMemoryByContent, billingAutoRechargeSection, billingCreditsSection, billingPlanSection, buildReportUrl, canChatWithEmbedMentor, cancelDeleteEvaluation, cancelDeleteInstance, cancelDeleteSkill, cancelDisconnectInstance, cancelEnableChatPrivacyMidSession, cancelKeyDelete, cancelLlmJudge, cancelNewInstanceDialog, cancelStartEvaluation, checkAdminStatus, checkRunStatus, clearDateRangeFilter, clearGradeResultOverride, clearInstanceSearch, clearUserFilter, clickBackHome, clickBillingAddCredits, clickBillingManageBilling, clickBillingManageUsage, clickBillingUpgrade, clickChatPrivacyToggle, clickDownloadAgain, clickManualDownloadLink, closeBenchmarkItemsDialog, closeCreditBalanceDropdown, closeEvaluationDetailDialog, closeKeyDetail, closeManageBenchmarksDialog, closeWithEsc, confirmEnableChatPrivacyMidSession, confirmKeyDelete, connectToInstance, copyEndpoint, createAuthSetup, createBenchmark, createEnvConfig, createInstance, createKey, createLink, createPlaywrightConfig, createSkill, createSupportTicketViaChatAndVerify, createTool, creditBalancePanel, creditBalancePlanBadge, creditBalanceTrigger, deleteEvaluation, deleteFirstMemory, deleteGraderCriterion, deleteInstance, deleteKey, deleteMemoryByContent, deleteQaItem, deleteResource, deleteSkill, deleteTask, disableSkill, disableSupport, disconnectInstance, editAgentPrompt, editGraderCriterion, editInstance, editLink, editSkill, editTextResource, editTool, enableSkill, enableSupport, expandReview, expandTrace, expectAllEndpointsVisible, expectBenchmarkListed, expectBillingAutoRechargeSection, expectBillingCreditsSection, expectBillingPlanSection, expectBillingTabForCurrentPlan, expectBillingTabForFreePlan, expectBillingTabForPremiumPlan, expectBillingTabForTrialPlan, expectCallConfigVisible, expectCallConfigVoiceTriggerShows, expectChatPrivacyConfirmDialogOpen, expectChatPrivacyLocked, expectChatPrivacySource, expectChatPrivacyState, expectChatPrivacyToggleVisible, expectCompletedTasks, expectCreditBalanceForCurrentPlan, expectCreditBalancePanelForFreePlan, expectCreditBalancePanelForPremiumPlan, expectCreditBalancePanelForTrialPlan, expectCreditBalanceVisibilityForTenant, expectDetailWaitingForTraces, expectEndpointUrl, expectEntitySelected, expectFailedTasks, expectGradeResultRow, expectGraderMisconfiguredWarning, expectGraderTotalPoints, expectKeyDeleteError, expectKeyInList, expectKeyNotInList, expectKeysEmpty, expectLastCriterionDeleteDisabled, expectLinkInList, expectLinkNotInList, expectLinkTargetUri, expectLinksEmpty, expectLogDetailsStatus, expectLogsForTask, expectLtiHeader, expectMentorVoiceTriggerShows, expectMessageInConversation, expectNoAccessibilityViolations, expectNoAccessibilityViolationsOnDialogs, expectNoBenchmarksNotice, expectNoLogsForSelectedTask, expectNoRepliesYet, expectNoTickets, expectOutputFilterEnabled, expectPrivacyFieldsHidden, expectPrivacyFieldsVisible, expectPrivateModeSelected, expectPrivateModeTabReady, expectQaItemListed, expectReviewInList, expectReviewStatus, expectRunInTable, expectRunNotInTable, expectRunStatus, expectRunsTableEmpty, expectScheduleStartTimeInPastError, expectScreenShareDisabledHint, expectSelectedBenchmark, expectSttSelectDisabled, expectTaskInList, expectTaskNotInList, expectTaskStatus, expectTasksEmpty, expectTenantChatPrivacyEnabled, expectTenantChatPrivacyVisible, expectTicketClosedNotice, expectTicketDescriptionContains, expectTicketInList, expectTicketStatusInList, expectToolInList, expectToolNotInList, expectToolsEmpty, expectTotalTasks, expectTraceScore, expectTtsSelectDisabled, expectVoiceProviderSelected, expectVoiceVisible, exportRunCsv, fillLinkName, fillToolForm, filterByAction, filterByActionAndVerify, filterByActor, filterByActorAndVerify, filterByDateRange, filterGradeResultsByEmail, filterTicketsByStatus, filterTicketsByUser, generateBrowserSetupProjects, generateProjectConfig, getAddItemsDialog, getAuditLogRowCount, getAvailableActors, getBenchmarkCombobox, getBenchmarkComboboxDropdown, getBenchmarkItemsDialog, getBillingAutoRechargeStatus, getBillingPlanLabel, getBrowserKey, getCallConfigForm, getChatInput, getChatPrivacyConfirmDialog, getChatPrivacyToggle, getCreateBenchmarkDialog, getCreateKeyButton, getCreateLinkButton, getCreateToolButton, getCreditBalancePlanLabel, getCreditBalanceRemaining, getCurrentModel, getCurrentTenantShowPaywall, getDeleteEvaluationDialog, getDeleteQaItemDialog, getEditAgentDialog, getEndpoint, getEndpointCopyButton, getEndpointUrl, getEntityChip, getEvaluationDetailDialog, getInstanceHealthLabel, getInstanceRowCount, getInstanceStatusLabel, getKeyActionsTrigger, getKeyCreateModal, getKeyCreateNameInput, getKeyDeleteModal, getKeyDetailModal, getKeyDetailNameInput, getKeyPublicJwkField, getKeyPublicKeyField, getKeyRow, getKeysEmptyState, getKeysSection, getLinkCopyTargetUriButton, getLinkEditButton, getLinkModal, getLinkNameInput, getLinkRow, getLinksEmptyState, getLinksSection, getLlmJudgeDialog, getLlmPickerDialog, getLtiSubTab, getManageBenchmarksButton, getManageBenchmarksDialog, getMemoryCount, getMentorIdFromUrl, getNewEvaluationButton, getOutputFilterSwitch, getPaginationInfo, getPrivateModeCard, getReplyComposer, getReviewRow, getRunRow, getScheduleTaskButton, getSearchInput, getSkillRowCount, getStartEvaluationDialog, getStatusFilter, getSupportInfoBox, getSupportTabTrigger, getSupportToggle, getTaskRow, getTenantChatPrivacyRow, getTenantChatPrivacySwitch, getTicketDescription, getTicketDetail, getTicketList, getTicketRow, getTicketRowByIndex, getToolEditButton, getToolKeySelect, getToolModal, getToolRow, getToolsEmptyState, getToolsSection, getTraceRow, getUserFilter, getVoiceProviderCard, getVoiceRow, goToFirstPage, goToLastPage, goToNextPage, goToPage, goToPreviousPage, goToSkillsListPage, graderTabBody, inviteUserTest, isEvaluationTabVisible, isFirefox, isGraderTabVisible, isGradingEnabled, isJSON, isLtiTabVisible, isMemoryTabVisible, isOnFirstPage, isOnLastPage, isPrivacyTabVisible, isPrivateModeTabVisible, isSandboxTabVisible, isScreenShareTabVisible, isSkillEnabled, isSupportEnabled, isSupportTabVisible, isTasksTabVisible, isVoiceTabVisible, logger, loginWithEmailAndPassword, loginWithMicrosoftIdp, navigateToAccountComponent, navigateToAuditLog, navigateToAuditLogAndWaitForData, navigateToDataReports, navigateToReportDownload, openAddItemsDialog, openAddMemoryDialog, openAgentPromptEditModal, openBenchmarkItems, openCallConfigVoicePicker, openCreateKeyModal, openCreateLinkModal, openCreateToolModal, openCreditBalanceDropdown, openEditInstanceDialog, openEditLinkModal, openEditSkillDialog, openEditToolModal, openFirstLogDetails, openInstanceActionsMenu, openKeyActionsMenu, openKeyDelete, openKeyDetail, openLLMProviderPicker, openManageBenchmarksDialog, openMentorVoicePicker, openNewInstanceDialog, openNewReviewForRun, openNewReviewFromDetail, openNewSkillDialog, openRunActionsMenu, openRunResults, openScheduleTaskDialog, openScreenSharePromptEditor, openSkillActionsMenu, openSlashSkillPicker, openStartEvaluationDialog, openTicket, overrideGradeResult, parseReportUrlParams, previewCallConfigVoiceInline, previewMentorVoiceInline, previewVoice, pushConfiguration, readEndpointUrl, readKeyPublicJwk, readKeyPublicKey, refreshEvaluationDetail, refreshTickets, reliableClick, reliableFill, removeSkillFromAgent, removeTraceScore, renameKey, replyToTicket, requestSupportTicketViaChat, resetCallConfig, retry, runConnectedInstanceChecks, runInstanceChecks, safeWaitForURL, saveCallConfig, saveGraderConfig, saveScreenSharePrompts, saveVoiceSettings, scheduleTask, searchBenchmarks, searchInstances, searchTasks, searchVoices, selectBenchmark, selectCallMode, selectDateFromCalendar, selectJudgeLlm, selectLLMModel, selectLlmProvider, selectPrivacyAction, selectPrivateMode, selectSlashSkill, selectSttProvider, selectTaskInList, selectToolKeySetMode, selectToolSigningKey, selectTtsProvider, selectVoice, selectVoiceProvider, sendAgentChatMessage, setBlockMessage, setCallLanguage, setEnableVideo, setEntitySelected, setGradingEnabled, setOutputFilterEnabled, setScreenSharePrompt, setSupportEnabled, setTenantChatPrivacyEnabled, setTicketStatus, setUseFunctionCallingEnabled, setupSandboxInstance, shouldAddNewRowWhenClickingAddRowButton, shouldAllowEditingCellValuesInCSVEditor, shouldCancelCombiningReports, shouldCloseCSVEditorWhenClickingCloseButton, shouldCloseCSVEditorWithoutSavingWhenClickingCancel, shouldCombineRecommendationReports, shouldDirectlyDownloadChatHistoryReportWithoutCSVEditor, shouldDisableOtherDownloadButtonsWhileGeneratingReport, shouldDisplayCSVInEditableTableFormat, shouldDisplayReportCards, shouldHaveCombinedReportDataTestIds, shouldOpenCSVEditorDialog, shouldOpenCSVEditorForUserMetadataReport, shouldSaveEditedCSVAndTriggerDownload, shouldShowCombiningReportsDialog, shouldVerifyCSVEditorDialogAccessibility, signUpWithEmailAndPassword, startEvaluation, submitLinkModal, submitLlmJudge, submitToolModal, switchToAddItemsSubTab, switchToAgentSkillsSubTab, switchToAvailableSkillsSubTab, switchToEvaluationTab, switchToGraderSubTab, switchToGraderTab, switchToLtiSubTab, switchToLtiTab, switchToMemoryTab, switchToPrivacyTab, switchToPrivateModeTab, switchToSandboxTab, switchToScreenShareTab, switchToSkillResourcesSubTab, switchToSkillsTab, switchToSupportTab, switchToTasksTab, switchToVoiceSubTab, switchToVoiceTab, teardownSandboxInstance, test, toggleAutoPush, toggleMemorySwitch, toggleSkill, toolFields, uploadAssetResource, uploadQaCsv, verifyAgentConfigPromptsVisible, verifyAgentSkillsEmptyState, verifyAuditLogEmptyState, verifyAuditLogEntryStructure, verifyAuditLogGenericError, verifyAuditLogLoading, verifyAuditLogPermissionError, verifyAuditLogTableVisible, verifyConnectDisabledForUnhealthy, verifyConnectedInstanceCard, verifyCurrentPage, verifyDonePhase, verifyDownloadingPhase, verifyErrorPhase, verifyInstanceTableEmpty, verifyInstanceTableVisible, verifyMemoryExists, verifyMemoryNotExists, verifyMemoryTabMemoriesList, verifyMemoryTabSettings, verifyPreparingPhase, verifySkillAdded, verifySkillVisible, verifySkillsEmptyState, verifySkillsTabVisible, waitForAgentChatResponse, waitForAuditLogDataLoaded, waitForBillingTabReady, waitForCreditBalanceLoaded, waitForDialogReady, waitForElementStable, waitForPageLoad, waitForPageReady, waitForReportDownload, waitForTicketInList };
|
|
7851
|
+
export { AuthFlowBuilder, CHAT_PRIVACY_LABELS, CustomReporter, EVALS_LABELS, GRADER_LABELS, LTI_LABELS, LTI_TEST_IDS, MailsacClient, PRIVACY_LABELS, SCREENSHARE_LABELS, SPEND_LIMITS_LABELS, SUPPORT_LABELS, TASKS_LABELS, VOICE_LABELS, addGraderCriterion, addManualScore, addMemory, addQaPairsManually, addSkillToAgent, addTextResource, addUserSpendLimit, addUserSpendLimitFromTenantBilling, agentLimitsRow, agentLimitsSection, archiveFirstMemory, archiveMemoryByContent, billingAutoRechargeSection, billingCreditsSection, billingPlanSection, buildReportUrl, canChatWithEmbedMentor, cancelDeleteEvaluation, cancelDeleteInstance, cancelDeleteSkill, cancelDisconnectInstance, cancelEnableChatPrivacyMidSession, cancelKeyDelete, cancelLlmJudge, cancelNewInstanceDialog, cancelStartEvaluation, checkAdminStatus, checkRunStatus, clearAgentLimitsFilter, clearDateRangeFilter, clearGradeResultOverride, clearInstanceSearch, clearUserFilter, clickBackHome, clickBillingAddCredits, clickBillingManageBilling, clickBillingManageUsage, clickBillingUpgrade, clickChatPrivacyToggle, clickDownloadAgain, clickManualDownloadLink, clickSetSpendLimitForFilteredAgent, closeAgentLimitsPopup, closeBenchmarkItemsDialog, closeCreditBalanceDropdown, closeEvaluationDetailDialog, closeKeyDetail, closeManageBenchmarksDialog, closeWithEsc, confirmEnableChatPrivacyMidSession, confirmKeyDelete, connectToInstance, copyEndpoint, createAgentSpendLimitViaFilter, createAuthSetup, createBenchmark, createEnvConfig, createInstance, createKey, createLink, createPlaywrightConfig, createSkill, createSupportTicketViaChatAndVerify, createTool, creditBalancePanel, creditBalancePlanBadge, creditBalanceTrigger, deleteAgentSpendLimit, deleteAgentSpendLimitFromTenantBilling, deleteEvaluation, deleteFirstMemory, deleteGraderCriterion, deleteInstance, deleteKey, deleteMemoryByContent, deleteQaItem, deleteResource, deleteSkill, deleteTask, deleteUserSpendLimit, deleteWorkspaceSpendLimit, disableSkill, disableSupport, disconnectInstance, editAgentPrompt, editGraderCriterion, editInstance, editLink, editSkill, editTextResource, editTool, editUserSpendLimit, enableSkill, enableSupport, expandReview, expandTrace, expectAgentLimitsRowContent, expectAllEndpointsVisible, expectBenchmarkListed, expectBillingAutoRechargeSection, expectBillingCreditsSection, expectBillingPlanSection, expectBillingTabForCurrentPlan, expectBillingTabForFreePlan, expectBillingTabForPremiumPlan, expectBillingTabForTrialPlan, expectCallConfigVisible, expectCallConfigVoiceTriggerShows, expectChatPrivacyConfirmDialogOpen, expectChatPrivacyLocked, expectChatPrivacySource, expectChatPrivacyState, expectChatPrivacyToggleVisible, expectCompletedTasks, expectCreditBalanceForCurrentPlan, expectCreditBalancePanelForFreePlan, expectCreditBalancePanelForPremiumPlan, expectCreditBalancePanelForTrialPlan, expectCreditBalanceVisibilityForTenant, expectDetailWaitingForTraces, expectEndpointUrl, expectEntitySelected, expectFailedTasks, expectGradeResultRow, expectGraderMisconfiguredWarning, expectGraderTotalPoints, expectKeyDeleteError, expectKeyInList, expectKeyNotInList, expectKeysEmpty, expectLastCriterionDeleteDisabled, expectLinkInList, expectLinkNotInList, expectLinkTargetUri, expectLinksEmpty, expectLogDetailsStatus, expectLogsForTask, expectLtiHeader, expectMentorVoiceTriggerShows, expectMessageInConversation, expectNoAccessibilityViolations, expectNoAccessibilityViolationsOnDialogs, expectNoBenchmarksNotice, expectNoLogsForSelectedTask, expectNoRepliesYet, expectNoTickets, expectOutputFilterEnabled, expectPrivacyFieldsHidden, expectPrivacyFieldsVisible, expectPrivateModeSelected, expectPrivateModeTabReady, expectQaItemListed, expectReviewInList, expectReviewStatus, expectRunInTable, expectRunNotInTable, expectRunStatus, expectRunsTableEmpty, expectScheduleStartTimeInPastError, expectScreenShareDisabledHint, expectSelectedBenchmark, expectSttSelectDisabled, expectTaskInList, expectTaskNotInList, expectTaskStatus, expectTasksEmpty, expectTenantChatPrivacyEnabled, expectTenantChatPrivacyVisible, expectTicketClosedNotice, expectTicketDescriptionContains, expectTicketInList, expectTicketStatusInList, expectToolInList, expectToolNotInList, expectToolsEmpty, expectTotalTasks, expectTraceScore, expectTtsSelectDisabled, expectVoiceProviderSelected, expectVoiceVisible, expectWorkspaceActualSpendStats, expectWorkspaceSpendStats, exportRunCsv, fillLinkName, fillToolForm, filterAgentLimits, filterByAction, filterByActionAndVerify, filterByActor, filterByActorAndVerify, filterByDateRange, filterGradeResultsByEmail, filterTicketsByStatus, filterTicketsByUser, generateBrowserSetupProjects, generateProjectConfig, getAddItemsDialog, getAuditLogRowCount, getAvailableActors, getBenchmarkCombobox, getBenchmarkComboboxDropdown, getBenchmarkItemsDialog, getBillingAutoRechargeStatus, getBillingPlanLabel, getBrowserKey, getCallConfigForm, getChatInput, getChatPrivacyConfirmDialog, getChatPrivacyToggle, getCreateBenchmarkDialog, getCreateKeyButton, getCreateLinkButton, getCreateToolButton, getCreditBalancePlanLabel, getCreditBalanceRemaining, getCurrentModel, getCurrentTenantShowPaywall, getDeleteEvaluationDialog, getDeleteQaItemDialog, getEditAgentDialog, getEndpoint, getEndpointCopyButton, getEndpointUrl, getEntityChip, getEvaluationDetailDialog, getInstanceHealthLabel, getInstanceRowCount, getInstanceStatusLabel, getKeyActionsTrigger, getKeyCreateModal, getKeyCreateNameInput, getKeyDeleteModal, getKeyDetailModal, getKeyDetailNameInput, getKeyPublicJwkField, getKeyPublicKeyField, getKeyRow, getKeysEmptyState, getKeysSection, getLinkCopyTargetUriButton, getLinkEditButton, getLinkModal, getLinkNameInput, getLinkRow, getLinksEmptyState, getLinksSection, getLlmJudgeDialog, getLlmPickerDialog, getLtiSubTab, getManageBenchmarksButton, getManageBenchmarksDialog, getMemoryCount, getMentorIdFromUrl, getNewEvaluationButton, getOutputFilterSwitch, getPaginationInfo, getPrivateModeCard, getReplyComposer, getReviewRow, getRunRow, getScheduleTaskButton, getSearchInput, getSkillRowCount, getStartEvaluationDialog, getStatusFilter, getSupportInfoBox, getSupportTabTrigger, getSupportToggle, getTaskRow, getTenantChatPrivacyRow, getTenantChatPrivacySwitch, getTicketDescription, getTicketDetail, getTicketList, getTicketRow, getTicketRowByIndex, getToolEditButton, getToolKeySelect, getToolModal, getToolRow, getToolsEmptyState, getToolsSection, getTraceRow, getUserFilter, getVoiceProviderCard, getVoiceRow, goToFirstPage, goToLastPage, goToNextPage, goToPage, goToPreviousPage, goToSkillsListPage, graderTabBody, inviteUserTest, isEvaluationTabVisible, isFirefox, isGraderTabVisible, isGradingEnabled, isJSON, isLtiTabVisible, isMemoryTabVisible, isOnFirstPage, isOnLastPage, isPrivacyTabVisible, isPrivateModeTabVisible, isSandboxTabVisible, isScreenShareTabVisible, isSkillEnabled, isSpendLimitsTabVisible, isSupportEnabled, isSupportTabVisible, isTasksTabVisible, isVoiceTabVisible, logger, loginWithEmailAndPassword, loginWithMicrosoftIdp, navigateToAccountComponent, navigateToAuditLog, navigateToAuditLogAndWaitForData, navigateToDataReports, navigateToReportDownload, openAddItemsDialog, openAddMemoryDialog, openAgentLimitsManage, openAgentPromptEditModal, openBenchmarkItems, openCallConfigVoicePicker, openCreateKeyModal, openCreateLinkModal, openCreateToolModal, openCreditBalanceDropdown, openEditInstanceDialog, openEditLinkModal, openEditSkillDialog, openEditToolModal, openFirstLogDetails, openInstanceActionsMenu, openKeyActionsMenu, openKeyDelete, openKeyDetail, openLLMProviderPicker, openManageBenchmarksDialog, openMentorVoicePicker, openNewInstanceDialog, openNewReviewForRun, openNewReviewFromDetail, openNewSkillDialog, openRunActionsMenu, openRunResults, openScheduleTaskDialog, openScreenSharePromptEditor, openSkillActionsMenu, openSlashSkillPicker, openStartEvaluationDialog, openTicket, overrideGradeResult, parseReportUrlParams, previewCallConfigVoiceInline, previewMentorVoiceInline, previewVoice, pushConfiguration, readEndpointUrl, readKeyPublicJwk, readKeyPublicKey, refreshEvaluationDetail, refreshTickets, reliableClick, reliableFill, removeSkillFromAgent, removeTraceScore, renameKey, replyToTicket, requestSupportTicketViaChat, resetCallConfig, retry, runConnectedInstanceChecks, runInstanceChecks, safeWaitForURL, saveCallConfig, saveGraderConfig, saveScreenSharePrompts, saveVoiceSettings, scheduleTask, searchBenchmarks, searchInstances, searchTasks, searchVoices, selectBenchmark, selectCallMode, selectDateFromCalendar, selectJudgeLlm, selectLLMModel, selectLlmProvider, selectPrivacyAction, selectPrivateMode, selectSlashSkill, selectSttProvider, selectTaskInList, selectToolKeySetMode, selectToolSigningKey, selectTtsProvider, selectVoice, selectVoiceProvider, sendAgentChatMessage, setAgentLimitsRowEnabled, setAgentSpendLimit, setAgentSpendLimitFromTenantBilling, setBlockMessage, setCallLanguage, setCatalogSkillEnabled, setEnableVideo, setEntitySelected, setGradingEnabled, setOutputFilterEnabled, setScreenSharePrompt, setSupportEnabled, setTenantChatPrivacyEnabled, setTicketStatus, setUseFunctionCallingEnabled, setUserSpendLimitEnabled, setWorkspaceSpendLimit, setupSandboxInstance, shouldAddNewRowWhenClickingAddRowButton, shouldAllowEditingCellValuesInCSVEditor, shouldCancelCombiningReports, shouldCloseCSVEditorWhenClickingCloseButton, shouldCloseCSVEditorWithoutSavingWhenClickingCancel, shouldCombineRecommendationReports, shouldDirectlyDownloadChatHistoryReportWithoutCSVEditor, shouldDisableOtherDownloadButtonsWhileGeneratingReport, shouldDisplayCSVInEditableTableFormat, shouldDisplayReportCards, shouldHaveCombinedReportDataTestIds, shouldOpenCSVEditorDialog, shouldOpenCSVEditorForUserMetadataReport, shouldSaveEditedCSVAndTriggerDownload, shouldShowCombiningReportsDialog, shouldVerifyCSVEditorDialogAccessibility, signUpWithEmailAndPassword, spendLimitsTabBody, startEvaluation, submitLinkModal, submitLlmJudge, submitToolModal, switchToAddItemsSubTab, switchToAgentLimits, switchToAgentSkillsSubTab, switchToAvailableSkillsSubTab, switchToEvaluationTab, switchToGraderSubTab, switchToGraderTab, switchToLtiSubTab, switchToLtiTab, switchToMemoryTab, switchToPlanAndCredits, switchToPrivacyTab, switchToPrivateModeTab, switchToSandboxTab, switchToScreenShareTab, switchToSkillResourcesSubTab, switchToSkillsTab, switchToSpendLimitsSubTab, switchToSpendLimitsTab, switchToSupportTab, switchToTasksTab, switchToVoiceSubTab, switchToVoiceTab, switchToWorkspaceSpendLimits, teardownSandboxInstance, test, toggleAutoPush, toggleMemorySwitch, toggleSkill, toolFields, uploadAssetResource, uploadQaCsv, userSpendLimitRow, verifyAgentConfigPromptsVisible, verifyAgentSkillsEmptyState, verifyAuditLogEmptyState, verifyAuditLogEntryStructure, verifyAuditLogGenericError, verifyAuditLogLoading, verifyAuditLogPermissionError, verifyAuditLogTableVisible, verifyConnectDisabledForUnhealthy, verifyConnectedInstanceCard, verifyCurrentPage, verifyDonePhase, verifyDownloadingPhase, verifyErrorPhase, verifyInstanceTableEmpty, verifyInstanceTableVisible, verifyMemoryExists, verifyMemoryNotExists, verifyMemoryTabMemoriesList, verifyMemoryTabSettings, verifyPreparingPhase, verifySkillAdded, verifySkillVisible, verifySkillsEmptyState, verifySkillsTabVisible, waitForAgentChatResponse, waitForAuditLogDataLoaded, waitForBillingTabReady, waitForCreditBalanceLoaded, waitForDialogReady, waitForElementStable, waitForPageLoad, waitForPageReady, waitForReportDownload, waitForTicketInList, workspaceSpendLimitSection };
|
|
7182
7852
|
//# sourceMappingURL=index.esm.js.map
|